Skip to content

Commit b242596

Browse files
committed
all: fix reflect.Value.Interface races
The reflect.Value.Interface method shallow copies the underlying value, which may copy mutexes and atomically-accessed fields. Some usages of the Interface method is only to check if the interface value implements an interface. In which case the shallow copy was unnecessary. Change those usages to use the reflect.Value.Implements method instead. Fixes #838
1 parent 6c65a55 commit b242596

File tree

3 files changed

+43
-5
lines changed

3 files changed

+43
-5
lines changed

jsonpb/jsonpb.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ type wkt interface {
165165
XXX_WellKnownType() string
166166
}
167167

168+
var (
169+
wktType = reflect.TypeOf((*wkt)(nil)).Elem()
170+
messageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
171+
)
172+
168173
// marshalObject writes a struct to the Writer.
169174
func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeURL string) error {
170175
if jsm, ok := v.(JSONPBMarshaler); ok {
@@ -531,7 +536,8 @@ func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v refle
531536

532537
// Handle well-known types.
533538
// Most are handled up in marshalObject (because 99% are messages).
534-
if wkt, ok := v.Interface().(wkt); ok {
539+
if v.Type().Implements(wktType) {
540+
wkt := v.Interface().(wkt)
535541
switch wkt.XXX_WellKnownType() {
536542
case "NullValue":
537543
out.write("null")
@@ -1277,8 +1283,8 @@ func checkRequiredFields(pb proto.Message) error {
12771283
}
12781284

12791285
func checkRequiredFieldsInValue(v reflect.Value) error {
1280-
if pm, ok := v.Interface().(proto.Message); ok {
1281-
return checkRequiredFields(pm)
1286+
if v.Type().Implements(messageType) {
1287+
return checkRequiredFields(v.Interface().(proto.Message))
12821288
}
12831289
return nil
12841290
}

proto/all_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -2490,3 +2490,33 @@ func BenchmarkUnmarshalUnrecognizedFields(b *testing.B) {
24902490
p2.Unmarshal(pbd)
24912491
}
24922492
}
2493+
2494+
// TestRace tests whether there are races among the different marshalers.
2495+
func TestRace(t *testing.T) {
2496+
m := &descriptorpb.FileDescriptorProto{
2497+
Options: &descriptorpb.FileOptions{
2498+
GoPackage: proto.String("path/to/my/package"),
2499+
},
2500+
}
2501+
2502+
wg := &sync.WaitGroup{}
2503+
defer wg.Wait()
2504+
2505+
wg.Add(1)
2506+
go func() {
2507+
defer wg.Done()
2508+
proto.Marshal(m)
2509+
}()
2510+
2511+
wg.Add(1)
2512+
go func() {
2513+
defer wg.Done()
2514+
(&jsonpb.Marshaler{}).MarshalToString(m)
2515+
}()
2516+
2517+
wg.Add(1)
2518+
go func() {
2519+
defer wg.Done()
2520+
m.String()
2521+
}()
2522+
}

proto/text.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
456456
return nil
457457
}
458458

459+
var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
460+
459461
// writeAny writes an arbitrary field.
460462
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
461463
v = reflect.Indirect(v)
@@ -519,8 +521,8 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
519521
// mutating this value.
520522
v = v.Addr()
521523
}
522-
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
523-
text, err := etm.MarshalText()
524+
if v.Type().Implements(textMarshalerType) {
525+
text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
524526
if err != nil {
525527
return err
526528
}

0 commit comments

Comments
 (0)