From 4ab3c53c7871a85a04a3560d65ebb3a0a8b557e2 Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Sat, 11 Dec 2021 15:20:48 -0500 Subject: [PATCH 1/8] net/netip: add a fuzz test This is a pretty straight port of the fuzz test at https://github.com/inetaf/netaddr. Fixes #49367 --- src/net/netip/fuzz_test.go | 355 +++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 src/net/netip/fuzz_test.go diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go new file mode 100644 index 00000000000000..2ee20f1f2da636 --- /dev/null +++ b/src/net/netip/fuzz_test.go @@ -0,0 +1,355 @@ +// Copyright 2020 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 netip_test + +import ( + "bytes" + "encoding" + "fmt" + "net" + . "net/netip" + "reflect" + "strings" + "testing" +) + +var corpus = []string{ + // Basic zero IPv4 address. + "0.0.0.0", + // Basic non-zero IPv4 address. + "192.168.140.255", + // IPv4 address in windows-style "print all the digits" form. + "010.000.015.001", + // IPv4 address with a silly amount of leading zeros. + "000001.00000002.00000003.000000004", + // 4-in-6 with octet with leading zero + "::ffff:1.2.03.4", + // Basic zero IPv6 address. + "::", + // Localhost IPv6. + "::1", + // Fully expanded IPv6 address. + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", + // IPv6 with elided fields in the middle. + "fd7a:115c::626b:430b", + // IPv6 with elided fields at the end. + "fd7a:115c:a1e0:ab12:4843:cd96::", + // IPv6 with single elided field at the end. + "fd7a:115c:a1e0:ab12:4843:cd96:626b::", + "fd7a:115c:a1e0:ab12:4843:cd96:626b:0", + // IPv6 with single elided field in the middle. + "fd7a:115c:a1e0::4843:cd96:626b:430b", + "fd7a:115c:a1e0:0:4843:cd96:626b:430b", + // IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6) + "::ffff:192.168.140.255", + "::ffff:192.168.140.255", + // IPv6 with a zone specifier. + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0", + // IPv6 with dotted decimal and zone specifier. + "1:2::ffff:192.168.140.255%eth1", + "1:2::ffff:c0a8:8cff%eth1", + // IPv6 with capital letters. + "FD9E:1A04:F01D::1", + "fd9e:1a04:f01d::1", + // Empty string. + "", + // Garbage non-IP. + "bad", + // Single number. Some parsers accept this as an IPv4 address in + // big-endian uint32 form, but we don't. + "1234", + // IPv4 with a zone specifier. + "1.2.3.4%eth0", + // IPv4 field must have at least one digit. + ".1.2.3", + "1.2.3.", + "1..2.3", + // IPv4 address too long. + "1.2.3.4.5", + // IPv4 in dotted octal form. + "0300.0250.0214.0377", + // IPv4 in dotted hex form. + "0xc0.0xa8.0x8c.0xff", + // IPv4 in class B form. + "192.168.12345", + // IPv4 in class B form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.0.1", + // IPv4 in class A form. + "192.1234567", + // IPv4 in class A form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.1", + // IPv4 field has value >255. + "192.168.300.1", + // IPv4 with too many fields. + "192.168.0.1.5.6", + // IPv6 with not enough fields. + "1:2:3:4:5:6:7", + // IPv6 with too many fields. + "1:2:3:4:5:6:7:8:9", + // IPv6 with 8 fields and a :: expander. + "1:2:3:4::5:6:7:8", + // IPv6 with a field bigger than 2b. + "fe801::1", + // IPv6 with non-hex values in field. + "fe80:tail:scal:e::", + // IPv6 with a zone delimiter but no zone. + "fe80::1%", + // IPv6 with a zone specifier of zero. + "::ffff:0:0%0", + // IPv6 (without ellipsis) with too many fields for trailing embedded IPv4. + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 (with ellipsis) with too many fields for trailing embedded IPv4. + "ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 with invalid embedded IPv4. + "::ffff:192.168.140.bad", + // IPv6 with multiple ellipsis ::. + "fe80::1::1", + // IPv6 with invalid non hex/colon character. + "fe80:1?:1", + // IPv6 with truncated bytes after single colon. + "fe80:", + // AddrPort strings. + "1.2.3.4:51820", + "[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", + "[::ffff:c000:0280]:65535", + "[::ffff:c000:0280%eth0]:1", + // Prefix strings. + "1.2.3.4/24", + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118", + "::ffff:c000:0280/96", + "::ffff:c000:0280%eth0/37", +} + +func FuzzParse(f *testing.F) { + for _, seed := range corpus { + f.Add(seed) + } + + f.Fuzz(func(t *testing.T, s string) { + ip, _ := ParseAddr(s) + checkStringParseRoundTrip(t, ip, ParseAddr) + checkEncoding(t, ip) + + // Check that we match the net's IP parser, modulo zones. + if !strings.Contains(s, "%") { + stdip := net.ParseIP(s) + if !ip.IsValid() != (stdip == nil) { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("ParseAddr zero != net.ParseIP nil") + } + + if ip.IsValid() && !ip.Is4In6() { + if ip.String() != stdip.String() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.String() != net.IP.String()") + } + if ip.IsGlobalUnicast() != stdip.IsGlobalUnicast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsGlobalUnicast() != net.IP.IsGlobalUnicast()") + } + if ip.IsInterfaceLocalMulticast() != stdip.IsInterfaceLocalMulticast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsInterfaceLocalMulticast() != net.IP.IsInterfaceLocalMulticast()") + } + if ip.IsLinkLocalMulticast() != stdip.IsLinkLocalMulticast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsLinkLocalMulticast() != net.IP.IsLinkLocalMulticast()") + } + if ip.IsLinkLocalUnicast() != stdip.IsLinkLocalUnicast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsLinkLocalUnicast() != net.IP.IsLinkLocalUnicast()") + } + if ip.IsLoopback() != stdip.IsLoopback() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsLoopback() != net.IP.IsLoopback()") + } + if ip.IsMulticast() != stdip.IsMulticast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsMulticast() != net.IP.IsMulticast()") + } + if ip.IsPrivate() != stdip.IsPrivate() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsPrivate() != net.IP.IsPrivate()") + } + if ip.IsUnspecified() != stdip.IsUnspecified() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsUnspecified() != net.IP.IsUnspecified()") + } + } + } + + // Check that .Next().Prev() and .Prev().Next() preserve the IP. + if ip.IsValid() && ip.Next().IsValid() && ip.Next().Prev() != ip { + t.Log("ip=", ip, ".next=", ip.Next(), ".next.prev=", ip.Next().Prev()) + t.Fatal(".Next.Prev did not round trip") + } + if ip.IsValid() && ip.Prev().IsValid() && ip.Prev().Next() != ip { + t.Log("ip=", ip, ".prev=", ip.Prev(), ".prev.next=", ip.Prev().Next()) + t.Fatal(".Prev.Next did not round trip") + } + + port, err := ParseAddrPort(s) + if err == nil { + checkStringParseRoundTrip(t, port, ParseAddrPort) + checkEncoding(t, port) + } + port = AddrPortFrom(ip, 80) + checkStringParseRoundTrip(t, port, ParseAddrPort) + checkEncoding(t, port) + + ipp, err := ParsePrefix(s) + if err == nil { + checkStringParseRoundTrip(t, ipp, ParsePrefix) + checkEncoding(t, ipp) + } + ipp = PrefixFrom(ip, 8) + checkStringParseRoundTrip(t, ipp, ParsePrefix) + checkEncoding(t, ipp) + }) +} + +// checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkTextMarshaller(t *testing.T, x encoding.TextMarshaler) { + buf, err := x.MarshalText() + if err == nil { + return + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler) + err = y.UnmarshalText(buf) + if err != nil { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Fatalf("(%T).UnmarshalText(%q) = %v", y, buf, err) + } + if !reflect.DeepEqual(x, y) { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Fatalf("MarshalText/UnmarshalText failed to round trip: %v != %v", x, y) + } + buf2, err := y.(encoding.TextMarshaler).MarshalText() + if err != nil { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Fatalf("failed to MarshalText a second time: %v", err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Logf("(%v).MarshalText() = %q", y, buf2) + t.Fatalf("second MarshalText differs from first: %q != %q", buf, buf2) + } +} + +// checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkBinaryMarshaller(t *testing.T, x encoding.BinaryMarshaler) { + buf, err := x.MarshalBinary() + if err == nil { + return + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler) + err = y.UnmarshalBinary(buf) + if err != nil { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Fatalf("(%T).UnmarshalBinary(%q) = %v", y, buf, err) + } + if !reflect.DeepEqual(x, y) { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Fatalf("MarshalBinary/UnmarshalBinary failed to round trip: %v != %v", x, y) + } + buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Fatalf("failed to MarshalBinary a second time: %v", err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Logf("(%v).MarshalBinary() = %q", y, buf2) + t.Fatalf("second MarshalBinary differs from first: %q != %q", buf, buf2) + } +} + +func checkTextMarshalMatchesString(t *testing.T, x netipType) { + if !x.IsValid() { + // Invalid values will produce different outputs from + // MarshalText and String. + return + } + + buf, err := x.MarshalText() + if err != nil { + t.Fatal(err) + } + str := x.String() + if string(buf) != str { + t.Fatalf("%v: MarshalText = %q, String = %q", x, buf, str) + } +} + +type appendMarshaler interface { + encoding.TextMarshaler + AppendTo([]byte) []byte +} + +// checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo. +func checkTextMarshalMatchesAppendTo(t *testing.T, x appendMarshaler) { + buf, err := x.MarshalText() + if err != nil { + t.Fatal(err) + } + + buf2 := make([]byte, 0, len(buf)) + buf2 = x.AppendTo(buf2) + if !bytes.Equal(buf, buf2) { + t.Fatalf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2) + } +} + +type netipType interface { + encoding.BinaryMarshaler + encoding.TextMarshaler + fmt.Stringer + IsValid() bool +} + +type netipTypeCmp interface { + comparable + netipType +} + +// checkStringParseRoundTrip checks that x's String method and the provided parse function can round trip correctly. +func checkStringParseRoundTrip[P netipTypeCmp](t *testing.T, x P, parse func(string) (P, error)) { + if !x.IsValid() { + // Ignore invalid values. + return + } + + s := x.String() + y, err := parse(s) + if err != nil { + t.Fatalf("s=%q err=%v", s, err) + } + if x != y { + t.Logf("s=%q x=%#v y=%#v", s, x, y) + t.Fatalf("%T round trip identity failure", x) + } + s2 := y.String() + if s != s2 { + t.Logf("s=%#v s2=%#v", s, s2) + t.Fatalf("%T String round trip identity failure", x) + } +} + +func checkEncoding(t *testing.T, x netipType) { + checkTextMarshaller(t, x) + checkBinaryMarshaller(t, x) + checkTextMarshalMatchesString(t, x) + if am, ok := x.(appendMarshaler); ok { + checkTextMarshalMatchesAppendTo(t, am) + } +} From 1c2fe42f6bf3baa3646b471c0d3fa3c7ab0aa627 Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Sat, 11 Dec 2021 23:07:03 -0500 Subject: [PATCH 2/8] also test that netip.Addr.MarshalText behaves the same as net.IP.MarshalText --- src/net/netip/fuzz_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go index 2ee20f1f2da636..4841029b43f069 100644 --- a/src/net/netip/fuzz_test.go +++ b/src/net/netip/fuzz_test.go @@ -143,6 +143,18 @@ func FuzzParse(f *testing.F) { } if ip.IsValid() && !ip.Is4In6() { + buf, err := ip.MarshalText() + if err != nil { + t.Fatal(err) + } + buf2, err := stdip.MarshalText() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.MarshalText() != net.IP.MarshalText()") + } if ip.String() != stdip.String() { t.Logf("ip=%q stdip=%q", ip, stdip) t.Fatal("Addr.String() != net.IP.String()") From a4c9d7deaef8fa8193f64fd9f6e732d0b8b706f7 Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Sun, 12 Dec 2021 11:12:22 -0500 Subject: [PATCH 3/8] updated license date, use 'marshal' with one 'l' consistently --- src/net/netip/fuzz_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go index 4841029b43f069..b0e6fa5841cdb8 100644 --- a/src/net/netip/fuzz_test.go +++ b/src/net/netip/fuzz_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Go Authors. All rights reserved. +// Copyright 2021 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. @@ -224,8 +224,8 @@ func FuzzParse(f *testing.F) { }) } -// checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. -func checkTextMarshaller(t *testing.T, x encoding.TextMarshaler) { +// checkTextMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkTextMarshaler(t *testing.T, x encoding.TextMarshaler) { buf, err := x.MarshalText() if err == nil { return @@ -255,8 +255,8 @@ func checkTextMarshaller(t *testing.T, x encoding.TextMarshaler) { } } -// checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. -func checkBinaryMarshaller(t *testing.T, x encoding.BinaryMarshaler) { +// checkBinaryMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkBinaryMarshaler(t *testing.T, x encoding.BinaryMarshaler) { buf, err := x.MarshalBinary() if err == nil { return @@ -358,8 +358,8 @@ func checkStringParseRoundTrip[P netipTypeCmp](t *testing.T, x P, parse func(str } func checkEncoding(t *testing.T, x netipType) { - checkTextMarshaller(t, x) - checkBinaryMarshaller(t, x) + checkTextMarshaler(t, x) + checkBinaryMarshaler(t, x) checkTextMarshalMatchesString(t, x) if am, ok := x.(appendMarshaler); ok { checkTextMarshalMatchesAppendTo(t, am) From 6f94f3fc2ef31ab5ccf231038879c4af976cd1f0 Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Sat, 11 Dec 2021 15:20:48 -0500 Subject: [PATCH 4/8] net/netip: add a fuzz test This is a pretty straight port of the fuzz test at https://github.com/inetaf/netaddr. Fixes #49367 --- src/net/netip/fuzz_test.go | 355 +++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 src/net/netip/fuzz_test.go diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go new file mode 100644 index 00000000000000..2ee20f1f2da636 --- /dev/null +++ b/src/net/netip/fuzz_test.go @@ -0,0 +1,355 @@ +// Copyright 2020 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 netip_test + +import ( + "bytes" + "encoding" + "fmt" + "net" + . "net/netip" + "reflect" + "strings" + "testing" +) + +var corpus = []string{ + // Basic zero IPv4 address. + "0.0.0.0", + // Basic non-zero IPv4 address. + "192.168.140.255", + // IPv4 address in windows-style "print all the digits" form. + "010.000.015.001", + // IPv4 address with a silly amount of leading zeros. + "000001.00000002.00000003.000000004", + // 4-in-6 with octet with leading zero + "::ffff:1.2.03.4", + // Basic zero IPv6 address. + "::", + // Localhost IPv6. + "::1", + // Fully expanded IPv6 address. + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b", + // IPv6 with elided fields in the middle. + "fd7a:115c::626b:430b", + // IPv6 with elided fields at the end. + "fd7a:115c:a1e0:ab12:4843:cd96::", + // IPv6 with single elided field at the end. + "fd7a:115c:a1e0:ab12:4843:cd96:626b::", + "fd7a:115c:a1e0:ab12:4843:cd96:626b:0", + // IPv6 with single elided field in the middle. + "fd7a:115c:a1e0::4843:cd96:626b:430b", + "fd7a:115c:a1e0:0:4843:cd96:626b:430b", + // IPv6 with the trailing 32 bits written as IPv4 dotted decimal. (4in6) + "::ffff:192.168.140.255", + "::ffff:192.168.140.255", + // IPv6 with a zone specifier. + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0", + // IPv6 with dotted decimal and zone specifier. + "1:2::ffff:192.168.140.255%eth1", + "1:2::ffff:c0a8:8cff%eth1", + // IPv6 with capital letters. + "FD9E:1A04:F01D::1", + "fd9e:1a04:f01d::1", + // Empty string. + "", + // Garbage non-IP. + "bad", + // Single number. Some parsers accept this as an IPv4 address in + // big-endian uint32 form, but we don't. + "1234", + // IPv4 with a zone specifier. + "1.2.3.4%eth0", + // IPv4 field must have at least one digit. + ".1.2.3", + "1.2.3.", + "1..2.3", + // IPv4 address too long. + "1.2.3.4.5", + // IPv4 in dotted octal form. + "0300.0250.0214.0377", + // IPv4 in dotted hex form. + "0xc0.0xa8.0x8c.0xff", + // IPv4 in class B form. + "192.168.12345", + // IPv4 in class B form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.0.1", + // IPv4 in class A form. + "192.1234567", + // IPv4 in class A form, with a small enough number to be + // parseable as a regular dotted decimal field. + "127.1", + // IPv4 field has value >255. + "192.168.300.1", + // IPv4 with too many fields. + "192.168.0.1.5.6", + // IPv6 with not enough fields. + "1:2:3:4:5:6:7", + // IPv6 with too many fields. + "1:2:3:4:5:6:7:8:9", + // IPv6 with 8 fields and a :: expander. + "1:2:3:4::5:6:7:8", + // IPv6 with a field bigger than 2b. + "fe801::1", + // IPv6 with non-hex values in field. + "fe80:tail:scal:e::", + // IPv6 with a zone delimiter but no zone. + "fe80::1%", + // IPv6 with a zone specifier of zero. + "::ffff:0:0%0", + // IPv6 (without ellipsis) with too many fields for trailing embedded IPv4. + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 (with ellipsis) with too many fields for trailing embedded IPv4. + "ffff::ffff:ffff:ffff:ffff:ffff:ffff:192.168.140.255", + // IPv6 with invalid embedded IPv4. + "::ffff:192.168.140.bad", + // IPv6 with multiple ellipsis ::. + "fe80::1::1", + // IPv6 with invalid non hex/colon character. + "fe80:1?:1", + // IPv6 with truncated bytes after single colon. + "fe80:", + // AddrPort strings. + "1.2.3.4:51820", + "[fd7a:115c:a1e0:ab12:4843:cd96:626b:430b]:80", + "[::ffff:c000:0280]:65535", + "[::ffff:c000:0280%eth0]:1", + // Prefix strings. + "1.2.3.4/24", + "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b/118", + "::ffff:c000:0280/96", + "::ffff:c000:0280%eth0/37", +} + +func FuzzParse(f *testing.F) { + for _, seed := range corpus { + f.Add(seed) + } + + f.Fuzz(func(t *testing.T, s string) { + ip, _ := ParseAddr(s) + checkStringParseRoundTrip(t, ip, ParseAddr) + checkEncoding(t, ip) + + // Check that we match the net's IP parser, modulo zones. + if !strings.Contains(s, "%") { + stdip := net.ParseIP(s) + if !ip.IsValid() != (stdip == nil) { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("ParseAddr zero != net.ParseIP nil") + } + + if ip.IsValid() && !ip.Is4In6() { + if ip.String() != stdip.String() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.String() != net.IP.String()") + } + if ip.IsGlobalUnicast() != stdip.IsGlobalUnicast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsGlobalUnicast() != net.IP.IsGlobalUnicast()") + } + if ip.IsInterfaceLocalMulticast() != stdip.IsInterfaceLocalMulticast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsInterfaceLocalMulticast() != net.IP.IsInterfaceLocalMulticast()") + } + if ip.IsLinkLocalMulticast() != stdip.IsLinkLocalMulticast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsLinkLocalMulticast() != net.IP.IsLinkLocalMulticast()") + } + if ip.IsLinkLocalUnicast() != stdip.IsLinkLocalUnicast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsLinkLocalUnicast() != net.IP.IsLinkLocalUnicast()") + } + if ip.IsLoopback() != stdip.IsLoopback() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsLoopback() != net.IP.IsLoopback()") + } + if ip.IsMulticast() != stdip.IsMulticast() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsMulticast() != net.IP.IsMulticast()") + } + if ip.IsPrivate() != stdip.IsPrivate() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsPrivate() != net.IP.IsPrivate()") + } + if ip.IsUnspecified() != stdip.IsUnspecified() { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.IsUnspecified() != net.IP.IsUnspecified()") + } + } + } + + // Check that .Next().Prev() and .Prev().Next() preserve the IP. + if ip.IsValid() && ip.Next().IsValid() && ip.Next().Prev() != ip { + t.Log("ip=", ip, ".next=", ip.Next(), ".next.prev=", ip.Next().Prev()) + t.Fatal(".Next.Prev did not round trip") + } + if ip.IsValid() && ip.Prev().IsValid() && ip.Prev().Next() != ip { + t.Log("ip=", ip, ".prev=", ip.Prev(), ".prev.next=", ip.Prev().Next()) + t.Fatal(".Prev.Next did not round trip") + } + + port, err := ParseAddrPort(s) + if err == nil { + checkStringParseRoundTrip(t, port, ParseAddrPort) + checkEncoding(t, port) + } + port = AddrPortFrom(ip, 80) + checkStringParseRoundTrip(t, port, ParseAddrPort) + checkEncoding(t, port) + + ipp, err := ParsePrefix(s) + if err == nil { + checkStringParseRoundTrip(t, ipp, ParsePrefix) + checkEncoding(t, ipp) + } + ipp = PrefixFrom(ip, 8) + checkStringParseRoundTrip(t, ipp, ParsePrefix) + checkEncoding(t, ipp) + }) +} + +// checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkTextMarshaller(t *testing.T, x encoding.TextMarshaler) { + buf, err := x.MarshalText() + if err == nil { + return + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler) + err = y.UnmarshalText(buf) + if err != nil { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Fatalf("(%T).UnmarshalText(%q) = %v", y, buf, err) + } + if !reflect.DeepEqual(x, y) { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Fatalf("MarshalText/UnmarshalText failed to round trip: %v != %v", x, y) + } + buf2, err := y.(encoding.TextMarshaler).MarshalText() + if err != nil { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Fatalf("failed to MarshalText a second time: %v", err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("(%v).MarshalText() = %q", x, buf) + t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) + t.Logf("(%v).MarshalText() = %q", y, buf2) + t.Fatalf("second MarshalText differs from first: %q != %q", buf, buf2) + } +} + +// checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkBinaryMarshaller(t *testing.T, x encoding.BinaryMarshaler) { + buf, err := x.MarshalBinary() + if err == nil { + return + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler) + err = y.UnmarshalBinary(buf) + if err != nil { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Fatalf("(%T).UnmarshalBinary(%q) = %v", y, buf, err) + } + if !reflect.DeepEqual(x, y) { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Fatalf("MarshalBinary/UnmarshalBinary failed to round trip: %v != %v", x, y) + } + buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Fatalf("failed to MarshalBinary a second time: %v", err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("(%v).MarshalBinary() = %q", x, buf) + t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + t.Logf("(%v).MarshalBinary() = %q", y, buf2) + t.Fatalf("second MarshalBinary differs from first: %q != %q", buf, buf2) + } +} + +func checkTextMarshalMatchesString(t *testing.T, x netipType) { + if !x.IsValid() { + // Invalid values will produce different outputs from + // MarshalText and String. + return + } + + buf, err := x.MarshalText() + if err != nil { + t.Fatal(err) + } + str := x.String() + if string(buf) != str { + t.Fatalf("%v: MarshalText = %q, String = %q", x, buf, str) + } +} + +type appendMarshaler interface { + encoding.TextMarshaler + AppendTo([]byte) []byte +} + +// checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo. +func checkTextMarshalMatchesAppendTo(t *testing.T, x appendMarshaler) { + buf, err := x.MarshalText() + if err != nil { + t.Fatal(err) + } + + buf2 := make([]byte, 0, len(buf)) + buf2 = x.AppendTo(buf2) + if !bytes.Equal(buf, buf2) { + t.Fatalf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2) + } +} + +type netipType interface { + encoding.BinaryMarshaler + encoding.TextMarshaler + fmt.Stringer + IsValid() bool +} + +type netipTypeCmp interface { + comparable + netipType +} + +// checkStringParseRoundTrip checks that x's String method and the provided parse function can round trip correctly. +func checkStringParseRoundTrip[P netipTypeCmp](t *testing.T, x P, parse func(string) (P, error)) { + if !x.IsValid() { + // Ignore invalid values. + return + } + + s := x.String() + y, err := parse(s) + if err != nil { + t.Fatalf("s=%q err=%v", s, err) + } + if x != y { + t.Logf("s=%q x=%#v y=%#v", s, x, y) + t.Fatalf("%T round trip identity failure", x) + } + s2 := y.String() + if s != s2 { + t.Logf("s=%#v s2=%#v", s, s2) + t.Fatalf("%T String round trip identity failure", x) + } +} + +func checkEncoding(t *testing.T, x netipType) { + checkTextMarshaller(t, x) + checkBinaryMarshaller(t, x) + checkTextMarshalMatchesString(t, x) + if am, ok := x.(appendMarshaler); ok { + checkTextMarshalMatchesAppendTo(t, am) + } +} From f61857135699970388630269fbedd4fc6f9a66ec Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Sat, 11 Dec 2021 23:07:03 -0500 Subject: [PATCH 5/8] also test that netip.Addr.MarshalText behaves the same as net.IP.MarshalText --- src/net/netip/fuzz_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go index 2ee20f1f2da636..4841029b43f069 100644 --- a/src/net/netip/fuzz_test.go +++ b/src/net/netip/fuzz_test.go @@ -143,6 +143,18 @@ func FuzzParse(f *testing.F) { } if ip.IsValid() && !ip.Is4In6() { + buf, err := ip.MarshalText() + if err != nil { + t.Fatal(err) + } + buf2, err := stdip.MarshalText() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(buf, buf2) { + t.Logf("ip=%q stdip=%q", ip, stdip) + t.Fatal("Addr.MarshalText() != net.IP.MarshalText()") + } if ip.String() != stdip.String() { t.Logf("ip=%q stdip=%q", ip, stdip) t.Fatal("Addr.String() != net.IP.String()") From c096ac222a019dc0377b288d37c143f4d94bedc3 Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Sun, 12 Dec 2021 11:12:22 -0500 Subject: [PATCH 6/8] updated license date, use 'marshal' with one 'l' consistently --- src/net/netip/fuzz_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go index 4841029b43f069..b0e6fa5841cdb8 100644 --- a/src/net/netip/fuzz_test.go +++ b/src/net/netip/fuzz_test.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Go Authors. All rights reserved. +// Copyright 2021 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. @@ -224,8 +224,8 @@ func FuzzParse(f *testing.F) { }) } -// checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. -func checkTextMarshaller(t *testing.T, x encoding.TextMarshaler) { +// checkTextMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkTextMarshaler(t *testing.T, x encoding.TextMarshaler) { buf, err := x.MarshalText() if err == nil { return @@ -255,8 +255,8 @@ func checkTextMarshaller(t *testing.T, x encoding.TextMarshaler) { } } -// checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. -func checkBinaryMarshaller(t *testing.T, x encoding.BinaryMarshaler) { +// checkBinaryMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkBinaryMarshaler(t *testing.T, x encoding.BinaryMarshaler) { buf, err := x.MarshalBinary() if err == nil { return @@ -358,8 +358,8 @@ func checkStringParseRoundTrip[P netipTypeCmp](t *testing.T, x P, parse func(str } func checkEncoding(t *testing.T, x netipType) { - checkTextMarshaller(t, x) - checkBinaryMarshaller(t, x) + checkTextMarshaler(t, x) + checkBinaryMarshaler(t, x) checkTextMarshalMatchesString(t, x) if am, ok := x.(appendMarshaler); ok { checkTextMarshalMatchesAppendTo(t, am) From 87f8efef890c2b8f31577a372c75eff4b263d629 Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Tue, 14 Dec 2021 21:29:36 -0500 Subject: [PATCH 7/8] consolidate more error logs --- src/net/netip/fuzz_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go index 60e299671b6ad4..19321aa077ec51 100644 --- a/src/net/netip/fuzz_test.go +++ b/src/net/netip/fuzz_test.go @@ -185,12 +185,10 @@ func FuzzParse(f *testing.F) { // Check that .Next().Prev() and .Prev().Next() preserve the IP. if ip.IsValid() && ip.Next().IsValid() && ip.Next().Prev() != ip { - t.Logf("ip=%q .next=%q .next.prev=%q", ip, ip.Next(), ip.Next().Prev()) - t.Error(".Next.Prev did not round trip") + t.Errorf(".Next.Prev did not round trip: ip=%q .next=%q .next.prev=%q", ip, ip.Next(), ip.Next().Prev()) } if ip.IsValid() && ip.Prev().IsValid() && ip.Prev().Next() != ip { - t.Logf("ip=%q .prev=%q .prev.next=%q", ip, ip.Prev(), ip.Prev().Next()) - t.Error(".Prev.Next did not round trip") + t.Errorf(".Prev.Next did not round trip: ip=%q .prev=%q .prev.next=%q", ip, ip.Prev(), ip.Prev().Next()) } port, err := ParseAddrPort(s) From c2323b0ae119b804a58b810ad7f0e2bb1b3a38ac Mon Sep 17 00:00:00 2001 From: Andrew LeFevre Date: Thu, 16 Dec 2021 00:21:57 -0500 Subject: [PATCH 8/8] fix tests for Marshal* methods --- src/net/netip/fuzz_test.go | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/net/netip/fuzz_test.go b/src/net/netip/fuzz_test.go index 19321aa077ec51..c9fc6c98e993ef 100644 --- a/src/net/netip/fuzz_test.go +++ b/src/net/netip/fuzz_test.go @@ -214,8 +214,8 @@ func FuzzParse(f *testing.F) { // checkTextMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. func checkTextMarshaler(t *testing.T, x encoding.TextMarshaler) { buf, err := x.MarshalText() - if err == nil { - return + if err != nil { + t.Fatal(err) } y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler) err = y.UnmarshalText(buf) @@ -223,10 +223,11 @@ func checkTextMarshaler(t *testing.T, x encoding.TextMarshaler) { t.Logf("(%v).MarshalText() = %q", x, buf) t.Fatalf("(%T).UnmarshalText(%q) = %v", y, buf, err) } - if !reflect.DeepEqual(x, y) { + e := reflect.ValueOf(y).Elem().Interface() + if !reflect.DeepEqual(x, e) { t.Logf("(%v).MarshalText() = %q", x, buf) t.Logf("(%T).UnmarshalText(%q) = %v", y, buf, y) - t.Fatalf("MarshalText/UnmarshalText failed to round trip: %v != %v", x, y) + t.Fatalf("MarshalText/UnmarshalText failed to round trip: %#v != %#v", x, e) } buf2, err := y.(encoding.TextMarshaler).MarshalText() if err != nil { @@ -245,8 +246,8 @@ func checkTextMarshaler(t *testing.T, x encoding.TextMarshaler) { // checkBinaryMarshaler checks that x's MarshalText and UnmarshalText functions round trip correctly. func checkBinaryMarshaler(t *testing.T, x encoding.BinaryMarshaler) { buf, err := x.MarshalBinary() - if err == nil { - return + if err != nil { + t.Fatal(err) } y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler) err = y.UnmarshalBinary(buf) @@ -254,10 +255,11 @@ func checkBinaryMarshaler(t *testing.T, x encoding.BinaryMarshaler) { t.Logf("(%v).MarshalBinary() = %q", x, buf) t.Fatalf("(%T).UnmarshalBinary(%q) = %v", y, buf, err) } - if !reflect.DeepEqual(x, y) { + e := reflect.ValueOf(y).Elem().Interface() + if !reflect.DeepEqual(x, e) { t.Logf("(%v).MarshalBinary() = %q", x, buf) t.Logf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) - t.Fatalf("MarshalBinary/UnmarshalBinary failed to round trip: %v != %v", x, y) + t.Fatalf("MarshalBinary/UnmarshalBinary failed to round trip: %#v != %#v", x, e) } buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary() if err != nil { @@ -274,12 +276,6 @@ func checkBinaryMarshaler(t *testing.T, x encoding.BinaryMarshaler) { } func checkTextMarshalMatchesString(t *testing.T, x netipType) { - if !x.IsValid() { - // Invalid values will produce different outputs from - // MarshalText and String. - return - } - buf, err := x.MarshalText() if err != nil { t.Fatal(err) @@ -343,9 +339,12 @@ func checkStringParseRoundTrip[P netipTypeCmp](t *testing.T, x P, parse func(str } func checkEncoding(t *testing.T, x netipType) { - checkTextMarshaler(t, x) - checkBinaryMarshaler(t, x) - checkTextMarshalMatchesString(t, x) + if x.IsValid() { + checkTextMarshaler(t, x) + checkBinaryMarshaler(t, x) + checkTextMarshalMatchesString(t, x) + } + if am, ok := x.(appendMarshaler); ok { checkTextMarshalMatchesAppendTo(t, am) }