1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsen// Package wirefuzz includes a fuzzer for the wire marshaler and unmarshaler. 6*1c12ee1eSDan Willemsenpackage wirefuzz 7*1c12ee1eSDan Willemsen 8*1c12ee1eSDan Willemsenimport ( 9*1c12ee1eSDan Willemsen "fmt" 10*1c12ee1eSDan Willemsen 11*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/impl" 12*1c12ee1eSDan Willemsen "google.golang.org/protobuf/proto" 13*1c12ee1eSDan Willemsen "google.golang.org/protobuf/reflect/protoregistry" 14*1c12ee1eSDan Willemsen piface "google.golang.org/protobuf/runtime/protoiface" 15*1c12ee1eSDan Willemsen 16*1c12ee1eSDan Willemsen fuzzpb "google.golang.org/protobuf/internal/testprotos/fuzz" 17*1c12ee1eSDan Willemsen) 18*1c12ee1eSDan Willemsen 19*1c12ee1eSDan Willemsen// Fuzz is a fuzzer for proto.Marshal and proto.Unmarshal. 20*1c12ee1eSDan Willemsenfunc Fuzz(data []byte) (score int) { 21*1c12ee1eSDan Willemsen // Unmarshal and Validate should agree about the validity of the message. 22*1c12ee1eSDan Willemsen m1 := &fuzzpb.Fuzz{} 23*1c12ee1eSDan Willemsen mt := m1.ProtoReflect().Type() 24*1c12ee1eSDan Willemsen _, valid := impl.Validate(mt, piface.UnmarshalInput{Buf: data}) 25*1c12ee1eSDan Willemsen if err := (proto.UnmarshalOptions{AllowPartial: true}).Unmarshal(data, m1); err != nil { 26*1c12ee1eSDan Willemsen switch valid { 27*1c12ee1eSDan Willemsen case impl.ValidationUnknown: 28*1c12ee1eSDan Willemsen case impl.ValidationInvalid: 29*1c12ee1eSDan Willemsen default: 30*1c12ee1eSDan Willemsen panic("unmarshal error with validation status: " + valid.String()) 31*1c12ee1eSDan Willemsen } 32*1c12ee1eSDan Willemsen return 0 33*1c12ee1eSDan Willemsen } 34*1c12ee1eSDan Willemsen switch valid { 35*1c12ee1eSDan Willemsen case impl.ValidationUnknown: 36*1c12ee1eSDan Willemsen case impl.ValidationValid: 37*1c12ee1eSDan Willemsen default: 38*1c12ee1eSDan Willemsen panic("unmarshal ok with validation status: " + valid.String()) 39*1c12ee1eSDan Willemsen } 40*1c12ee1eSDan Willemsen 41*1c12ee1eSDan Willemsen // Unmarshal, Validate, and CheckInitialized should agree about initialization. 42*1c12ee1eSDan Willemsen checkInit := proto.CheckInitialized(m1) == nil 43*1c12ee1eSDan Willemsen methods := m1.ProtoReflect().ProtoMethods() 44*1c12ee1eSDan Willemsen in := piface.UnmarshalInput{Message: mt.New(), Resolver: protoregistry.GlobalTypes, Depth: 10000} 45*1c12ee1eSDan Willemsen if checkInit { 46*1c12ee1eSDan Willemsen // If the message initialized, the both Unmarshal and Validate should 47*1c12ee1eSDan Willemsen // report it as such. False negatives are tolerated, but have a 48*1c12ee1eSDan Willemsen // significant impact on performance. In general, they should always 49*1c12ee1eSDan Willemsen // properly determine initialization for any normalized message, 50*1c12ee1eSDan Willemsen // we produce by re-marshaling the message. 51*1c12ee1eSDan Willemsen in.Buf, _ = proto.Marshal(m1) 52*1c12ee1eSDan Willemsen if out, _ := methods.Unmarshal(in); out.Flags&piface.UnmarshalInitialized == 0 { 53*1c12ee1eSDan Willemsen panic("unmarshal reports initialized message as partial") 54*1c12ee1eSDan Willemsen } 55*1c12ee1eSDan Willemsen if out, _ := impl.Validate(mt, in); out.Flags&piface.UnmarshalInitialized == 0 { 56*1c12ee1eSDan Willemsen panic("validate reports initialized message as partial") 57*1c12ee1eSDan Willemsen } 58*1c12ee1eSDan Willemsen } else { 59*1c12ee1eSDan Willemsen // If the message is partial, then neither Unmarshal nor Validate 60*1c12ee1eSDan Willemsen // should ever report it as such. False positives are unacceptable. 61*1c12ee1eSDan Willemsen in.Buf = data 62*1c12ee1eSDan Willemsen if out, _ := methods.Unmarshal(in); out.Flags&piface.UnmarshalInitialized != 0 { 63*1c12ee1eSDan Willemsen panic("unmarshal reports partial message as initialized") 64*1c12ee1eSDan Willemsen } 65*1c12ee1eSDan Willemsen if out, _ := impl.Validate(mt, in); out.Flags&piface.UnmarshalInitialized != 0 { 66*1c12ee1eSDan Willemsen panic("validate reports partial message as initialized") 67*1c12ee1eSDan Willemsen } 68*1c12ee1eSDan Willemsen } 69*1c12ee1eSDan Willemsen 70*1c12ee1eSDan Willemsen // Round-trip Marshal and Unmarshal should produce the same messages. 71*1c12ee1eSDan Willemsen data1, err := proto.MarshalOptions{AllowPartial: !checkInit}.Marshal(m1) 72*1c12ee1eSDan Willemsen if err != nil { 73*1c12ee1eSDan Willemsen panic(err) 74*1c12ee1eSDan Willemsen } 75*1c12ee1eSDan Willemsen if proto.Size(m1) != len(data1) { 76*1c12ee1eSDan Willemsen panic(fmt.Errorf("size does not match output: %d != %d", proto.Size(m1), len(data1))) 77*1c12ee1eSDan Willemsen } 78*1c12ee1eSDan Willemsen m2 := &fuzzpb.Fuzz{} 79*1c12ee1eSDan Willemsen if err := (proto.UnmarshalOptions{AllowPartial: !checkInit}).Unmarshal(data1, m2); err != nil { 80*1c12ee1eSDan Willemsen panic(err) 81*1c12ee1eSDan Willemsen } 82*1c12ee1eSDan Willemsen if !proto.Equal(m1, m2) { 83*1c12ee1eSDan Willemsen panic("not equal") 84*1c12ee1eSDan Willemsen } 85*1c12ee1eSDan Willemsen return 1 86*1c12ee1eSDan Willemsen} 87