xref: /aosp_15_r20/external/golang-protobuf/internal/fuzz/wirefuzz/fuzz.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
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