aboutsummaryrefslogtreecommitdiff
path: root/internal/fuzz/wirefuzz/fuzz.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/fuzz/wirefuzz/fuzz.go')
-rw-r--r--internal/fuzz/wirefuzz/fuzz.go86
1 files changed, 86 insertions, 0 deletions
diff --git a/internal/fuzz/wirefuzz/fuzz.go b/internal/fuzz/wirefuzz/fuzz.go
new file mode 100644
index 00000000..fd27cca6
--- /dev/null
+++ b/internal/fuzz/wirefuzz/fuzz.go
@@ -0,0 +1,86 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package wirefuzz includes a fuzzer for the wire marshaler and unmarshaler.
+package wirefuzz
+
+import (
+ "fmt"
+
+ "google.golang.org/protobuf/internal/impl"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/reflect/protoregistry"
+ piface "google.golang.org/protobuf/runtime/protoiface"
+
+ fuzzpb "google.golang.org/protobuf/internal/testprotos/fuzz"
+)
+
+// Fuzz is a fuzzer for proto.Marshal and proto.Unmarshal.
+func Fuzz(data []byte) (score int) {
+ // Unmarshal and Validate should agree about the validity of the message.
+ m1 := &fuzzpb.Fuzz{}
+ mt := m1.ProtoReflect().Type()
+ _, valid := impl.Validate(mt, piface.UnmarshalInput{Buf: data})
+ if err := (proto.UnmarshalOptions{AllowPartial: true}).Unmarshal(data, m1); err != nil {
+ switch valid {
+ case impl.ValidationUnknown:
+ case impl.ValidationInvalid:
+ default:
+ panic("unmarshal error with validation status: " + valid.String())
+ }
+ return 0
+ }
+ switch valid {
+ case impl.ValidationUnknown:
+ case impl.ValidationValid:
+ default:
+ panic("unmarshal ok with validation status: " + valid.String())
+ }
+
+ // Unmarshal, Validate, and CheckInitialized should agree about initialization.
+ checkInit := proto.CheckInitialized(m1) == nil
+ methods := m1.ProtoReflect().ProtoMethods()
+ in := piface.UnmarshalInput{Message: mt.New(), Resolver: protoregistry.GlobalTypes, Depth: 10000}
+ if checkInit {
+ // If the message initialized, the both Unmarshal and Validate should
+ // report it as such. False negatives are tolerated, but have a
+ // significant impact on performance. In general, they should always
+ // properly determine initialization for any normalized message,
+ // we produce by re-marshaling the message.
+ in.Buf, _ = proto.Marshal(m1)
+ if out, _ := methods.Unmarshal(in); out.Flags&piface.UnmarshalInitialized == 0 {
+ panic("unmarshal reports initialized message as partial")
+ }
+ if out, _ := impl.Validate(mt, in); out.Flags&piface.UnmarshalInitialized == 0 {
+ panic("validate reports initialized message as partial")
+ }
+ } else {
+ // If the message is partial, then neither Unmarshal nor Validate
+ // should ever report it as such. False positives are unacceptable.
+ in.Buf = data
+ if out, _ := methods.Unmarshal(in); out.Flags&piface.UnmarshalInitialized != 0 {
+ panic("unmarshal reports partial message as initialized")
+ }
+ if out, _ := impl.Validate(mt, in); out.Flags&piface.UnmarshalInitialized != 0 {
+ panic("validate reports partial message as initialized")
+ }
+ }
+
+ // Round-trip Marshal and Unmarshal should produce the same messages.
+ data1, err := proto.MarshalOptions{AllowPartial: !checkInit}.Marshal(m1)
+ if err != nil {
+ panic(err)
+ }
+ if proto.Size(m1) != len(data1) {
+ panic(fmt.Errorf("size does not match output: %d != %d", proto.Size(m1), len(data1)))
+ }
+ m2 := &fuzzpb.Fuzz{}
+ if err := (proto.UnmarshalOptions{AllowPartial: !checkInit}).Unmarshal(data1, m2); err != nil {
+ panic(err)
+ }
+ if !proto.Equal(m1, m2) {
+ panic("not equal")
+ }
+ return 1
+}