fuzz.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2020 The Inet.Af 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. //go:build gofuzz
  5. // +build gofuzz
  6. package netaddr
  7. import (
  8. "bytes"
  9. "encoding"
  10. "fmt"
  11. "net"
  12. "reflect"
  13. "strings"
  14. )
  15. func Fuzz(b []byte) int {
  16. s := string(b)
  17. ip, _ := ParseIP(s)
  18. checkStringParseRoundTrip(ip, parseIP)
  19. checkEncoding(ip)
  20. // Check that we match the standard library's IP parser, modulo zones.
  21. if !strings.Contains(s, "%") {
  22. stdip := net.ParseIP(s)
  23. if ip.IsZero() != (stdip == nil) {
  24. fmt.Println("stdip=", stdip, "ip=", ip)
  25. panic("net.ParseIP nil != ParseIP zero")
  26. } else if !ip.IsZero() && !ip.Is4in6() && ip.String() != stdip.String() {
  27. fmt.Println("ip=", ip, "stdip=", stdip)
  28. panic("net.IP.String() != IP.String()")
  29. }
  30. }
  31. // Check that .Next().Prior() and .Prior().Next() preserve the IP.
  32. if !ip.IsZero() && !ip.Next().IsZero() && ip.Next().Prior() != ip {
  33. fmt.Println("ip=", ip, ".next=", ip.Next(), ".next.prior=", ip.Next().Prior())
  34. panic(".Next.Prior did not round trip")
  35. }
  36. if !ip.IsZero() && !ip.Prior().IsZero() && ip.Prior().Next() != ip {
  37. fmt.Println("ip=", ip, ".prior=", ip.Prior(), ".prior.next=", ip.Prior().Next())
  38. panic(".Prior.Next did not round trip")
  39. }
  40. port, err := ParseIPPort(s)
  41. if err == nil {
  42. checkStringParseRoundTrip(port, parseIPPort)
  43. checkEncoding(port)
  44. }
  45. port = IPPortFrom(ip, 80)
  46. checkStringParseRoundTrip(port, parseIPPort)
  47. checkEncoding(port)
  48. ipp, err := ParseIPPrefix(s)
  49. if err == nil {
  50. checkStringParseRoundTrip(ipp, parseIPPrefix)
  51. checkEncoding(ipp)
  52. }
  53. ipp = IPPrefixFrom(ip, 8)
  54. checkStringParseRoundTrip(ipp, parseIPPrefix)
  55. checkEncoding(ipp)
  56. return 0
  57. }
  58. // Hopefully some of these generic helpers will eventually make their way to the standard library.
  59. // See https://github.com/golang/go/issues/46268.
  60. // checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly.
  61. func checkTextMarshaller(x encoding.TextMarshaler) {
  62. buf, err := x.MarshalText()
  63. if err == nil {
  64. return
  65. }
  66. y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler)
  67. err = y.UnmarshalText(buf)
  68. if err != nil {
  69. fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
  70. panic(fmt.Sprintf("(%T).UnmarshalText(%q) = %v", y, buf, err))
  71. }
  72. if !reflect.DeepEqual(x, y) {
  73. fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
  74. fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y)
  75. panic(fmt.Sprintf("MarshalText/UnmarshalText failed to round trip: %v != %v", x, y))
  76. }
  77. buf2, err := y.(encoding.TextMarshaler).MarshalText()
  78. if err != nil {
  79. fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
  80. fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y)
  81. panic(fmt.Sprintf("failed to MarshalText a second time: %v", err))
  82. }
  83. if !bytes.Equal(buf, buf2) {
  84. fmt.Printf("(%v).MarshalText() = %q\n", x, buf)
  85. fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y)
  86. fmt.Printf("(%v).MarshalText() = %q\n", y, buf2)
  87. panic(fmt.Sprintf("second MarshalText differs from first: %q != %q", buf, buf2))
  88. }
  89. }
  90. // checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly.
  91. func checkBinaryMarshaller(x encoding.BinaryMarshaler) {
  92. buf, err := x.MarshalBinary()
  93. if err == nil {
  94. return
  95. }
  96. y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler)
  97. err = y.UnmarshalBinary(buf)
  98. if err != nil {
  99. fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
  100. panic(fmt.Sprintf("(%T).UnmarshalBinary(%q) = %v", y, buf, err))
  101. }
  102. if !reflect.DeepEqual(x, y) {
  103. fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
  104. fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
  105. panic(fmt.Sprintf("MarshalBinary/UnmarshalBinary failed to round trip: %v != %v", x, y))
  106. }
  107. buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary()
  108. if err != nil {
  109. fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
  110. fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
  111. panic(fmt.Sprintf("failed to MarshalBinary a second time: %v", err))
  112. }
  113. if !bytes.Equal(buf, buf2) {
  114. fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf)
  115. fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y)
  116. fmt.Printf("(%v).MarshalBinary() = %q\n", y, buf2)
  117. panic(fmt.Sprintf("second MarshalBinary differs from first: %q != %q", buf, buf2))
  118. }
  119. }
  120. // fuzzAppendMarshaler is identical to appendMarshaler, defined in netaddr_test.go.
  121. // We have two because the two go-fuzz implementations differ
  122. // in whether they include _test.go files when typechecking.
  123. // We need this fuzz file to compile with and without netaddr_test.go,
  124. // which means defining the interface twice.
  125. type fuzzAppendMarshaler interface {
  126. encoding.TextMarshaler
  127. AppendTo([]byte) []byte
  128. }
  129. // checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo.
  130. func checkTextMarshalMatchesAppendTo(x fuzzAppendMarshaler) {
  131. buf, err := x.MarshalText()
  132. if err != nil {
  133. panic(err)
  134. }
  135. buf2 := make([]byte, 0, len(buf))
  136. buf2 = x.AppendTo(buf2)
  137. if !bytes.Equal(buf, buf2) {
  138. panic(fmt.Sprintf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2))
  139. }
  140. }
  141. // parseType are trampoline functions that give ParseType functions the same signature.
  142. // This would be nicer with generics.
  143. func parseIP(s string) (interface{}, error) { return ParseIP(s) }
  144. func parseIPPort(s string) (interface{}, error) { return ParseIPPort(s) }
  145. func parseIPPrefix(s string) (interface{}, error) { return ParseIPPrefix(s) }
  146. func checkStringParseRoundTrip(x fmt.Stringer, parse func(string) (interface{}, error)) {
  147. v, vok := x.(interface{ IsValid() bool })
  148. if vok && !v.IsValid() {
  149. // Ignore invalid values.
  150. return
  151. }
  152. // Zero values tend to print something like "invalid <TYPE>", so it's OK if they don't round trip.
  153. // The exception is if they have a Valid method and that Valid method
  154. // explicitly says that the zero value is valid.
  155. z, zok := x.(interface{ IsZero() bool })
  156. if zok && z.IsZero() && !(vok && v.IsValid()) {
  157. return
  158. }
  159. s := x.String()
  160. y, err := parse(s)
  161. if err != nil {
  162. panic(fmt.Sprintf("s=%q err=%v", s, err))
  163. }
  164. if !reflect.DeepEqual(x, y) {
  165. fmt.Printf("s=%q x=%#v y=%#v\n", s, x, y)
  166. panic(fmt.Sprintf("%T round trip identity failure", x))
  167. }
  168. s2 := y.(fmt.Stringer).String()
  169. if s != s2 {
  170. fmt.Printf("s=%#v s2=%#v\n", s, s2)
  171. panic(fmt.Sprintf("%T String round trip identity failure", x))
  172. }
  173. }
  174. func checkEncoding(x interface{}) {
  175. if tm, ok := x.(encoding.TextMarshaler); ok {
  176. checkTextMarshaller(tm)
  177. }
  178. if bm, ok := x.(encoding.BinaryMarshaler); ok {
  179. checkBinaryMarshaller(bm)
  180. }
  181. if am, ok := x.(fuzzAppendMarshaler); ok {
  182. checkTextMarshalMatchesAppendTo(am)
  183. }
  184. }
  185. // TODO: add helpers that check that String matches MarshalText for non-zero-ish values