...

Source file src/google.golang.org/protobuf/internal/fuzz/wirefuzz/fuzz.go

Documentation: google.golang.org/protobuf/internal/fuzz/wirefuzz

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

View as plain text