Congratulations on opening your first change. Thank you for your contribution!
Next steps:
A maintainer will review your change and provide feedback. See
https://go.dev/doc/contribute#review for more info and tips to get your
patch through code review.
Most changes in the Go project go through a few rounds of revision. This can be
surprising to people new to the project. The careful, iterative review process
is our way of helping mentor contributors and ensuring that their contributions
have a lasting impact.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Lasse Folger has uploaded this change for review.
go proto: Implement depth limit for unmarshaling
+ This change introduce a default and configurable depth limit for
proto.Unmarshal. If a messages is nested deeper than the limit,
unmarshaling will fail. There are two ways to nest messages. Either by
having fields which are message types itself or by using groups.
+ The default limit is 10,000 for now. This might change in the future
to align it with other language implementation (C++ and Java use 100
as limit).
+ If pure groups (groups that don't contain message fields) are nested
deeper than the default limit the unmarshaling fails with:
proto: cannot parse invalid wire-format data
+ Note: the configured limit does not apply to pure groups.
Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037
---
M encoding/protowire/wire.go
M internal/fuzz/wirefuzz/fuzz.go
M internal/impl/decode.go
M proto/decode.go
M reflect/protoreflect/methods.go
M runtime/protoiface/methods.go
6 files changed, 59 insertions(+), 3 deletions(-)
diff --git a/encoding/protowire/wire.go b/encoding/protowire/wire.go
index a427f8b..6374406 100644
--- a/encoding/protowire/wire.go
+++ b/encoding/protowire/wire.go
@@ -25,6 +25,7 @@
FirstReservedNumber Number = 19000
LastReservedNumber Number = 19999
MaxValidNumber Number = 1<<29 - 1
+ DefaultDepthLimit = 10000
)
// IsValid reports whether the field number is semantically valid.
@@ -55,6 +56,7 @@
errCodeOverflow
errCodeReserved
errCodeEndGroup
+ errCodeRecursionDepth
)
var (
@@ -112,6 +114,10 @@
// When parsing a group, the length includes the end group marker and
// the end group is verified to match the starting field number.
func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) {
+ return consumeFieldValueD(num, typ, b, DefaultDepthLimit)
+}
+
+func consumeFieldValueD(num Number, typ Type, b []byte, depth int) (n int) {
switch typ {
case VarintType:
_, n = ConsumeVarint(b)
@@ -126,6 +132,9 @@
_, n = ConsumeBytes(b)
return n
case StartGroupType:
+ if depth < 0 {
+ return errCodeRecursionDepth
+ }
n0 := len(b)
for {
num2, typ2, n := ConsumeTag(b)
@@ -140,7 +149,7 @@
return n0 - len(b)
}
- n = ConsumeFieldValue(num2, typ2, b)
+ n = consumeFieldValueD(num2, typ2, b, depth-1)
if n < 0 {
return n // forward error code
}
diff --git a/internal/fuzz/wirefuzz/fuzz.go b/internal/fuzz/wirefuzz/fuzz.go
index f7a9b74..fd27cca 100644
--- a/internal/fuzz/wirefuzz/fuzz.go
+++ b/internal/fuzz/wirefuzz/fuzz.go
@@ -41,7 +41,7 @@
// Unmarshal, Validate, and CheckInitialized should agree about initialization.
checkInit := proto.CheckInitialized(m1) == nil
methods := m1.ProtoReflect().ProtoMethods()
- in := piface.UnmarshalInput{Message: mt.New(), Resolver: protoregistry.GlobalTypes}
+ in := piface.UnmarshalInput{Message: mt.New(), Resolver: protoregistry.GlobalTypes, Depth: 10000}
if checkInit {
// If the message initialized, the both Unmarshal and Validate should
// report it as such. False negatives are tolerated, but have a
diff --git a/internal/impl/decode.go b/internal/impl/decode.go
index 949dc49..5ff6932 100644
--- a/internal/impl/decode.go
+++ b/internal/impl/decode.go
@@ -18,6 +18,7 @@
)
var errDecode = errors.New("cannot parse invalid wire-format data")
+var errRecursionDepth = errors.New("exceeded maximum recursion depth")
type unmarshalOptions struct {
flags protoiface.UnmarshalInputFlags
@@ -25,6 +26,7 @@
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
+ depth int
}
func (o unmarshalOptions) Options() proto.UnmarshalOptions {
@@ -44,6 +46,7 @@
var lazyUnmarshalOptions = unmarshalOptions{
resolver: preg.GlobalTypes,
+ depth: protowire.DefaultDepthLimit,
}
type unmarshalOutput struct {
@@ -62,6 +65,7 @@
out, err := mi.unmarshalPointer(in.Buf, p, 0, unmarshalOptions{
flags: in.Flags,
resolver: in.Resolver,
+ depth: in.Depth,
})
var flags piface.UnmarshalOutputFlags
if out.initialized {
@@ -82,6 +86,10 @@
func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) {
mi.init()
+ opts.depth--
+ if opts.depth < 0 {
+ return out, errRecursionDepth
+ }
if flags.ProtoLegacy && mi.isMessageSet {
return unmarshalMessageSet(mi, b, p, opts)
}
diff --git a/proto/decode.go b/proto/decode.go
index 49f9b8c..f0f76f5 100644
--- a/proto/decode.go
+++ b/proto/decode.go
@@ -42,18 +42,26 @@
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
+ // MaxRecursionDepth, if messages are nested deeper than this limit,
+ // the unmarshaling will abort.
+ MaxRecursionDepth int
}
// Unmarshal parses the wire-format message in b and places the result in m.
// The provided message must be mutable (e.g., a non-nil pointer to a message).
func Unmarshal(b []byte, m Message) error {
- _, err := UnmarshalOptions{}.unmarshal(b, m.ProtoReflect())
+ // Could call UnmarshalOptions.Unmarshal here to not have to set the
+ // MaxRecursionDepth explicitly.
+ _, err := UnmarshalOptions{MaxRecursionDepth: protowire.DefaultDepthLimit}.unmarshal(b, m.ProtoReflect())
return err
}
// Unmarshal parses the wire-format message in b and places the result in m.
// The provided message must be mutable (e.g., a non-nil pointer to a message).
func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error {
+ if o.MaxRecursionDepth == 0 {
+ o.MaxRecursionDepth = protowire.DefaultDepthLimit
+ }
_, err := o.unmarshal(b, m.ProtoReflect())
return err
}
@@ -63,6 +71,9 @@
// This method permits fine-grained control over the unmarshaler.
// Most users should use Unmarshal instead.
func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) {
+ if o.MaxRecursionDepth == 0 {
+ o.MaxRecursionDepth = protowire.DefaultDepthLimit
+ }
return o.unmarshal(in.Buf, in.Message)
}
@@ -86,12 +97,17 @@
Message: m,
Buf: b,
Resolver: o.Resolver,
+ Depth: o.MaxRecursionDepth,
}
if o.DiscardUnknown {
in.Flags |= protoiface.UnmarshalDiscardUnknown
}
out, err = methods.Unmarshal(in)
} else {
+ o.MaxRecursionDepth--
+ if o.MaxRecursionDepth < 0 {
+ return out, errors.New("exceeded max recursion depth")
+ }
err = o.unmarshalMessageSlow(b, m)
}
if err != nil {
diff --git a/reflect/protoreflect/methods.go b/reflect/protoreflect/methods.go
index 6be5d16..d5d5af6 100644
--- a/reflect/protoreflect/methods.go
+++ b/reflect/protoreflect/methods.go
@@ -53,6 +53,7 @@
FindExtensionByName(field FullName) (ExtensionType, error)
FindExtensionByNumber(message FullName, field FieldNumber) (ExtensionType, error)
}
+ Depth int
}
unmarshalOutput = struct {
pragma.NoUnkeyedLiterals
diff --git a/runtime/protoiface/methods.go b/runtime/protoiface/methods.go
index 32c04f6..44cf467 100644
--- a/runtime/protoiface/methods.go
+++ b/runtime/protoiface/methods.go
@@ -103,6 +103,7 @@
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
+ Depth int
}
// UnmarshalOutput is output from the Unmarshal method.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Lasse Folger.
Patch set 1:Code-Review +1
1 comment:
Patchset:
LGTM (I had reviewed it earlier as an internal change too).
If there is any external issue documenting the "why" of this change (robustness, security), then it would make sense to mention that in the commit description in the proper form so that this change and the issue are updated at the same time.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Lasse Folger.
6 comments:
Commit Message:
Patch Set #1, Line 7: go proto: Implement depth limit for unmarshaling
all: implement depth limit for unmarshaling
(Convention in this repo is to prepend the summary with the affected package(s), or "all" if the change is sufficiently broad.)
Patch Set #1, Line 10: messages
nitpick: s/If a messages/If a message/
File proto/decode.go:
Add a blank line separating this from the previous field.
Patch Set #1, Line 46: // the unmarshaling will abort.
MaxRecursionDepth limits how deeply messages may be nested.
If zero, a default limit is applied.
Patch Set #1, Line 47: MaxRecursionDepth int
C++ and Java call this setting "RecursionLimit". We should follow suit.
Patch Set #1, Line 54: // MaxRecursionDepth explicitly.
Either call UnmarshalOptions.Unmarshal or don't, but saying could you do it without saying why you don't is more confusing than informative.
(If you do call it, you should make sure the inner unmarshal call is still inlined.)
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Lasse Folger.
Patch set 1:Trust +1
Attention is currently required from: Lasse Folger.
Patch set 1:Run-TryBot +1
Attention is currently required from: Lasse Folger.
Patch set 1:Trust +1
1 comment:
Patchset:
Note that Run-TryBot vote has no effect in this repository, because its current configuration does not have TryBots enabled (i.e., CoordinatorCanBuild is false at https://cs.opensource.google/go/x/build/+/master:repos/repos.go;l=103-108;drc=62f0361b30b065e3c1e9ff530c7735d85526e893).
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Dmitri Shuralyov, Lasse Folger.
1 comment:
Patchset:
Note that Run-TryBot vote has no effect in this repository, because its current configuration does n […]
Yes, this repo has CI via GibHub actions (IIRC), but that regrettably doesn't run on pending CLs (AFAIK).
The GitHub CI is set up to test with a variety of Go versions, which trybots didn't support at the time Joe configured it. (And still don't, so far as I know.)
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Damien Neil, Dmitri Shuralyov, Lasse Folger.
1 comment:
Patchset:
Yes, this repo has CI via GibHub actions (IIRC), but that regrettably doesn't run on pending CLs (AFAIK).
Correct on both statements. https://github.com/protocolbuffers/protobuf-go/actions
It would be nice if TryBot could just run integration test script that at the roto of repo. There was some reason in the past that this wasn't possible, and I can't remember what that was.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Dmitri Shuralyov, Lasse Folger.
Lasse Folger uploaded patch set #2 to this change.
all: Implement depth limit for unmarshaling
+ This change introduce a default and configurable depth limit for
proto.Unmarshal. If a message is nested deeper than the limit,
unmarshaling will fail. There are two ways to nest messages. Either by
having fields which are message types itself or by using groups.
+ The default limit is 10,000 for now. This might change in the future
to align it with other language implementation (C++ and Java use 100
as limit).
+ If pure groups (groups that don't contain message fields) are nested
deeper than the default limit the unmarshaling fails with:
proto: cannot parse invalid wire-format data
+ Note: the configured limit does not apply to pure groups.
+ This change is introduced to improve security and robustness. Because
unmarshaling is implemented using recursion it can lead to stack overflows
for certain inputs. The introduced limit protects against this.
+ A secondary motivation for this limit is the alignment with other
languages. Protocol buffers are a language interoperability mechanism
and thus either all implementations should accept the input or all
implementation should reject the input.
Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037
---
M encoding/protowire/wire.go
M internal/fuzz/wirefuzz/fuzz.go
M internal/impl/decode.go
M proto/decode.go
M reflect/protoreflect/methods.go
M runtime/protoiface/methods.go
6 files changed, 65 insertions(+), 3 deletions(-)
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Joe Tsai.
8 comments:
Commit Message:
Patch Set #1, Line 7: go proto: Implement depth limit for unmarshaling
all: implement depth limit for unmarshaling […]
Done
Patch Set #1, Line 10: messages
nitpick: s/If a messages/If a message/
Done
Patchset:
LGTM (I had reviewed it earlier as an internal change too). […]
I don't think this is documented specifically for Go somewhere. I exetended the description to explain the motivation for this change.
> Yes, this repo has CI via GibHub actions (IIRC), but that regrettably doesn't run on pending CLs ( […]
FWIW, when I run `git codereview mail` the integration test is executed.
I assume there are ways to circumvent this but at least the default case is that these tests are executed (probably not covering all environment though).
File proto/decode.go:
Add a blank line separating this from the previous field.
Done
Patch Set #1, Line 46: // the unmarshaling will abort.
MaxRecursionDepth limits how deeply messages may be nested. […]
Done
Patch Set #1, Line 47: MaxRecursionDepth int
C++ and Java call this setting "RecursionLimit". We should follow suit.
Done
Patch Set #1, Line 54: // MaxRecursionDepth explicitly.
Either call UnmarshalOptions. […]
Done
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Lasse Folger, Joe Tsai.
Patch set 2:Code-Review +1
1 comment:
File encoding/protowire/wire.go:
Patch Set #2, Line 28: DefaultDepthLimit
Perhaps also 'DefaultRecursionLimit'.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Lasse Folger, Joe Tsai.
Lasse Folger uploaded patch set #3 to this change.
6 files changed, 69 insertions(+), 7 deletions(-)
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Joe Tsai, Chressie Himpel.
1 comment:
File encoding/protowire/wire.go:
Patch Set #2, Line 28: DefaultDepthLimit
Perhaps also 'DefaultRecursionLimit'.
Done
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Lasse Folger, Joe Tsai, Chressie Himpel.
Patch set 3:Trust +1
2 comments:
Commit Message:
Nitpick: s/I/i/ as Go commit message style uses lower-case verb after the colon.
See https://go.dev/doc/contribute#commit_messages (and https://go.dev/wiki/CommitMessage) for reference.
Patchset:
FWIW, when I run `git codereview mail` the integration test is executed.
I'm not aware this was a possibility, and it doesn't seem to be a documented feature of the mail subcommand. Is this behavior happening with the latest version of the git-codereview binary (v1.0.3 now, based on https://pkg.go.dev/golang.org/x/review/git-codereview?tab=versions)? If so, perhaps there's a git-hook coming from somewhere that triggers it.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Lasse Folger, Joe Tsai, Chressie Himpel.
Lasse Folger uploaded patch set #4 to this change.
all: implement depth limit for unmarshaling
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Dmitri Shuralyov, Joe Tsai, Chressie Himpel.
2 comments:
Commit Message:
Nitpick: s/I/i/ as Go commit message style uses lower-case verb after the colon. […]
Done
Patchset:
> FWIW, when I run `git codereview mail` the integration test is executed. […]
I followed the documentation here which seems to set this up:
https://github.com/protocolbuffers/protobuf-go/blob/master/CONTRIBUTING.md#sending-a-change-via-gerrit
(I probably should have read what I executed instead of blindly copy&pasting ;)
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Damien Neil, Lasse Folger, Joe Tsai, Chressie Himpel.
Patch set 4:Trust +1
1 comment:
Patchset:
I followed the documentation here which seems to set this up: […]
Ah, I see, so the steps in the README of this particular repository include installing such a hook. This isn't the case in most other Go repos, and I wasn't aware. I should've read its README. :) Thanks for helping me understand.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Nicolas Hillegeer, Lasse Folger, Joe Tsai, Chressie Himpel.
Patch set 4:Code-Review +2Trust +1
Attention is currently required from: Lasse Folger, Joe Tsai, Chressie Himpel.
Patch set 4:Code-Review +1
Attention is currently required from: Lasse Folger, Joe Tsai.
Patch set 4:Code-Review +1
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.
Damien Neil submitted this change.
all: implement depth limit for unmarshaling
+ This change introduce a default and configurable depth limit for
proto.Unmarshal. If a message is nested deeper than the limit,
unmarshaling will fail. There are two ways to nest messages. Either by
having fields which are message types itself or by using groups.
+ The default limit is 10,000 for now. This might change in the future
to align it with other language implementation (C++ and Java use 100
as limit).
+ If pure groups (groups that don't contain message fields) are nested
deeper than the default limit the unmarshaling fails with:
proto: cannot parse invalid wire-format data
+ Note: the configured limit does not apply to pure groups.
+ This change is introduced to improve security and robustness. Because
unmarshaling is implemented using recursion it can lead to stack overflows
for certain inputs. The introduced limit protects against this.
+ A secondary motivation for this limit is the alignment with other
languages. Protocol buffers are a language interoperability mechanism
and thus either all implementations should accept the input or all
implementation should reject the input.
Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/385854
Trust: Dmitri Shuralyov <dmit...@golang.org>
Reviewed-by: Damien Neil <dn...@google.com>
Trust: Damien Neil <dn...@google.com>
Reviewed-by: Nicolas Hillegeer <ak...@google.com>
Reviewed-by: Chressie Himpel <chre...@google.com>
---
M encoding/protowire/wire.go
M internal/fuzz/wirefuzz/fuzz.go
M internal/impl/decode.go
M proto/decode.go
M reflect/protoreflect/methods.go
M runtime/protoiface/methods.go
6 files changed, 75 insertions(+), 7 deletions(-)
diff --git a/encoding/protowire/wire.go b/encoding/protowire/wire.go
index a427f8b..9c61112 100644
--- a/encoding/protowire/wire.go
+++ b/encoding/protowire/wire.go
@@ -21,10 +21,11 @@
type Number int32
const (
- MinValidNumber Number = 1
- FirstReservedNumber Number = 19000
- LastReservedNumber Number = 19999
- MaxValidNumber Number = 1<<29 - 1
+ MinValidNumber Number = 1
+ FirstReservedNumber Number = 19000
+ LastReservedNumber Number = 19999
+ MaxValidNumber Number = 1<<29 - 1
+ DefaultRecursionLimit = 10000
)
// IsValid reports whether the field number is semantically valid.
@@ -55,6 +56,7 @@
errCodeOverflow
errCodeReserved
errCodeEndGroup
+ errCodeRecursionDepth
)
var (
@@ -112,6 +114,10 @@
// When parsing a group, the length includes the end group marker and
// the end group is verified to match the starting field number.
func ConsumeFieldValue(num Number, typ Type, b []byte) (n int) {
+ return consumeFieldValueD(num, typ, b, DefaultRecursionLimit)index 949dc49..c65b032 100644
--- a/internal/impl/decode.go
+++ b/internal/impl/decode.go
@@ -18,6 +18,7 @@
)
var errDecode = errors.New("cannot parse invalid wire-format data")
+var errRecursionDepth = errors.New("exceeded maximum recursion depth")
type unmarshalOptions struct {
flags protoiface.UnmarshalInputFlags
@@ -25,6 +26,7 @@
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
+ depth int
}
func (o unmarshalOptions) Options() proto.UnmarshalOptions {
@@ -44,6 +46,7 @@
var lazyUnmarshalOptions = unmarshalOptions{
resolver: preg.GlobalTypes,
+ depth: protowire.DefaultRecursionLimit,
}
type unmarshalOutput struct {
@@ -62,6 +65,7 @@
out, err := mi.unmarshalPointer(in.Buf, p, 0, unmarshalOptions{
flags: in.Flags,
resolver: in.Resolver,
+ depth: in.Depth,
})
var flags piface.UnmarshalOutputFlags
if out.initialized {
@@ -82,6 +86,10 @@
func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) {
mi.init()
+ opts.depth--
+ if opts.depth < 0 {
+ return out, errRecursionDepth
+ }
if flags.ProtoLegacy && mi.isMessageSet {
return unmarshalMessageSet(mi, b, p, opts)
}
diff --git a/proto/decode.go b/proto/decode.go
index 49f9b8c..11bf717 100644
--- a/proto/decode.go
+++ b/proto/decode.go
@@ -42,18 +42,25 @@
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
+
+ // RecursionLimit limits how deeply messages may be nested.
+ // If zero, a default limit is applied.
+ RecursionLimit int
}
// Unmarshal parses the wire-format message in b and places the result in m.
// The provided message must be mutable (e.g., a non-nil pointer to a message).
func Unmarshal(b []byte, m Message) error {
- _, err := UnmarshalOptions{}.unmarshal(b, m.ProtoReflect())
+ _, err := UnmarshalOptions{RecursionLimit: protowire.DefaultRecursionLimit}.unmarshal(b, m.ProtoReflect())
return err
}
// Unmarshal parses the wire-format message in b and places the result in m.
// The provided message must be mutable (e.g., a non-nil pointer to a message).
func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error {
+ if o.RecursionLimit == 0 {
+ o.RecursionLimit = protowire.DefaultRecursionLimit
+ }
_, err := o.unmarshal(b, m.ProtoReflect())
return err
}
@@ -63,6 +70,9 @@
// This method permits fine-grained control over the unmarshaler.
// Most users should use Unmarshal instead.
func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) {
+ if o.RecursionLimit == 0 {
+ o.RecursionLimit = protowire.DefaultRecursionLimit
+ }
return o.unmarshal(in.Buf, in.Message)
}
@@ -86,12 +96,17 @@
Message: m,
Buf: b,
Resolver: o.Resolver,
+ Depth: o.RecursionLimit,
}
if o.DiscardUnknown {
in.Flags |= protoiface.UnmarshalDiscardUnknown
}
out, err = methods.Unmarshal(in)
} else {
+ o.RecursionLimit--
+ if o.RecursionLimit < 0 {
+ return out, errors.New("exceeded max recursion depth")
+ }
err = o.unmarshalMessageSlow(b, m)
}
if err != nil {
diff --git a/reflect/protoreflect/methods.go b/reflect/protoreflect/methods.go
index 6be5d16..d5d5af6 100644
--- a/reflect/protoreflect/methods.go
+++ b/reflect/protoreflect/methods.go
@@ -53,6 +53,7 @@
FindExtensionByName(field FullName) (ExtensionType, error)
FindExtensionByNumber(message FullName, field FieldNumber) (ExtensionType, error)
}
+ Depth int
}
unmarshalOutput = struct {
pragma.NoUnkeyedLiterals
diff --git a/runtime/protoiface/methods.go b/runtime/protoiface/methods.go
index 32c04f6..44cf467 100644
--- a/runtime/protoiface/methods.go
+++ b/runtime/protoiface/methods.go
@@ -103,6 +103,7 @@
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
}
+ Depth int
}
// UnmarshalOutput is output from the Unmarshal method.
To view, visit change 385854. To unsubscribe, or for help writing mail filters, visit settings.