KEMBAR78
[protoreflect] Message.WhichOneof for generated message type panics when given synthetic oneof · Issue #1659 · golang/protobuf · GitHub
Skip to content

[protoreflect] Message.WhichOneof for generated message type panics when given synthetic oneof #1659

@jhump

Description

@jhump

What version of protobuf and what language are you using?
v1.36.0

What did you do?
Here's a reproducer test case.
protocolbuffers/protobuf-go@master...jhump:protobuf-go:jh/panic-repro
The panic happens in the call to msg.WhichOneof (line 20).

What did you expect to see?
In prior versions, at least as recently as 1.34.0, this worked as expected. It would return nil if the underlying proto3 optional field was present and otherwise it would return its field descriptor.

What did you see instead?

panic: invalid oneof descriptor goproto.proto.test3.TestAllTypes._optional_int32 for message goproto.proto.test3.TestAllTypes [recovered]
	panic: invalid oneof descriptor goproto.proto.test3.TestAllTypes._optional_int32 for message goproto.proto.test3.TestAllTypes

goroutine 21 [running]:
testing.tRunner.func1.2({0x10096da60, 0x1400008f2a0})
	/Users/jhumphries/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.4.darwin-arm64/src/testing/testing.go:1632 +0x1bc
testing.tRunner.func1()
	/Users/jhumphries/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.4.darwin-arm64/src/testing/testing.go:1635 +0x334
panic({0x10096da60?, 0x1400008f2a0?})
	/Users/jhumphries/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.4.darwin-arm64/src/runtime/panic.go:785 +0x124
google.golang.org/protobuf/internal/impl.(*messageState).WhichOneof(0x140000fc908, {0x1009cb318, 0x1400014a098})
	/Users/jhumphries/src/protobuf-go/internal/impl/message_reflect_gen.go:126 +0x194
google.golang.org/protobuf/reflect/protoreflect_test.TestSyntheticOneof(0x140000ac9c0)
	/Users/jhumphries/src/protobuf-go/reflect/protoreflect/oneof_test.go:20 +0x108
testing.tRunner(0x140000ac9c0, 0x1009c5868)
	/Users/jhumphries/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.4.darwin-arm64/src/testing/testing.go:1690 +0xe4
created by testing.(*T).Run in goroutine 1
	/Users/jhumphries/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.4.darwin-arm64/src/testing/testing.go:1743 +0x314

Anything else we should know about your project / environment?
Occurs on both OS X and Linux.

The following patch fixes the issue:

diff --git a/internal/impl/message_reflect_gen.go b/internal/impl/message_reflect_gen.go
index 99dc23c6..f12524be 100644
--- a/internal/impl/message_reflect_gen.go
+++ b/internal/impl/message_reflect_gen.go
@@ -123,6 +123,17 @@ func (m *messageState) WhichOneof(od protoreflect.OneofDescriptor) protoreflect.
        if oi := mi.oneofs[od.Name()]; oi != nil && oi.oneofDesc == od {
                return od.Fields().ByNumber(oi.which(m.pointer()))
        }
+       if od.Parent() == m.Descriptor() && od.IsSynthetic() {
+               // Synthetic oneofs are absent from mi.oneofs, so fallback to slow way.
+               fields := od.Fields()
+               for i, length := 0, fields.Len(); i < length; i++ {
+                       fd := fields.Get(i)
+                       if m.Has(fd) {
+                               return fd
+                       }
+               }
+               return nil
+       }
        panic("invalid oneof descriptor " + string(od.FullName()) + " for message " + string(m.Descriptor().FullName()))
 }
 func (m *messageState) GetUnknown() protoreflect.RawFields {

However, the file in question (internal/impl/message_reflect_gen.go) is generated. So a real fix will have to be made in the code generator or perhaps to reverse whatever change caused synthetic oneofs to no longer appear in mi.oneofs.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions