From 051bdf3fd12a40307606ff9381138039c5f452f0 Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <bracewell@google.com>
Date: Tue, 28 May 2024 13:26:31 -0700
Subject: [PATCH] [release-branch.go1.21] net/netip: check if address is v6
 mapped in Is methods

In all of the Is* methods, check if the address is a v6 mapped v4
address, and unmap it if so.

Thanks to Enze Wang of Alioth (@zer0yu) and Jianjun Chen of Zhongguancun
Lab (@chenjj) for reporting this issue.

Fixes #67680
Fixes #67681
Fixes CVE-2024-24790

Change-Id: I6bd03ca1a5d93a0b59027d861c84060967b265b0
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/1460
Reviewed-by: Russ Cox <rsc@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
(cherry picked from commit f7f270c1621fdc7ee48e0487b2fac0356947d19b)
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/1500
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/590315
Auto-Submit: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
---
 src/net/netip/inlining_test.go |  2 --
 src/net/netip/netip.go         | 26 +++++++++++++++++-
 src/net/netip/netip_test.go    | 50 +++++++++++++++++++++++++++++++---
 3 files changed, 71 insertions(+), 7 deletions(-)

diff --git a/src/net/netip/inlining_test.go b/src/net/netip/inlining_test.go
index b521eeebfd8f3..98584b098df1b 100644
--- a/src/net/netip/inlining_test.go
+++ b/src/net/netip/inlining_test.go
@@ -36,8 +36,6 @@ func TestInlining(t *testing.T) {
 		"Addr.Is4",
 		"Addr.Is4In6",
 		"Addr.Is6",
-		"Addr.IsLoopback",
-		"Addr.IsMulticast",
 		"Addr.IsInterfaceLocalMulticast",
 		"Addr.IsValid",
 		"Addr.IsUnspecified",
diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go
index a44b09495549d..9e4d41f8fb7b9 100644
--- a/src/net/netip/netip.go
+++ b/src/net/netip/netip.go
@@ -507,6 +507,10 @@ func (ip Addr) hasZone() bool {
 
 // IsLinkLocalUnicast reports whether ip is a link-local unicast address.
 func (ip Addr) IsLinkLocalUnicast() bool {
+	if ip.Is4In6() {
+		ip = ip.Unmap()
+	}
+
 	// Dynamic Configuration of IPv4 Link-Local Addresses
 	// https://datatracker.ietf.org/doc/html/rfc3927#section-2.1
 	if ip.Is4() {
@@ -522,6 +526,10 @@ func (ip Addr) IsLinkLocalUnicast() bool {
 
 // IsLoopback reports whether ip is a loopback address.
 func (ip Addr) IsLoopback() bool {
+	if ip.Is4In6() {
+		ip = ip.Unmap()
+	}
+
 	// Requirements for Internet Hosts -- Communication Layers (3.2.1.3 Addressing)
 	// https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3
 	if ip.Is4() {
@@ -537,6 +545,10 @@ func (ip Addr) IsLoopback() bool {
 
 // IsMulticast reports whether ip is a multicast address.
 func (ip Addr) IsMulticast() bool {
+	if ip.Is4In6() {
+		ip = ip.Unmap()
+	}
+
 	// Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES)
 	// https://datatracker.ietf.org/doc/html/rfc1112#section-4
 	if ip.Is4() {
@@ -555,7 +567,7 @@ func (ip Addr) IsMulticast() bool {
 func (ip Addr) IsInterfaceLocalMulticast() bool {
 	// IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses)
 	// https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
-	if ip.Is6() {
+	if ip.Is6() && !ip.Is4In6() {
 		return ip.v6u16(0)&0xff0f == 0xff01
 	}
 	return false // zero value
@@ -563,6 +575,10 @@ func (ip Addr) IsInterfaceLocalMulticast() bool {
 
 // IsLinkLocalMulticast reports whether ip is a link-local multicast address.
 func (ip Addr) IsLinkLocalMulticast() bool {
+	if ip.Is4In6() {
+		ip = ip.Unmap()
+	}
+
 	// IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24))
 	// https://datatracker.ietf.org/doc/html/rfc5771#section-4
 	if ip.Is4() {
@@ -591,6 +607,10 @@ func (ip Addr) IsGlobalUnicast() bool {
 		return false
 	}
 
+	if ip.Is4In6() {
+		ip = ip.Unmap()
+	}
+
 	// Match package net's IsGlobalUnicast logic. Notably private IPv4 addresses
 	// and ULA IPv6 addresses are still considered "global unicast".
 	if ip.Is4() && (ip == IPv4Unspecified() || ip == AddrFrom4([4]byte{255, 255, 255, 255})) {
@@ -608,6 +628,10 @@ func (ip Addr) IsGlobalUnicast() bool {
 // ip is in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or fc00::/7. This is the
 // same as net.IP.IsPrivate.
 func (ip Addr) IsPrivate() bool {
+	if ip.Is4In6() {
+		ip = ip.Unmap()
+	}
+
 	// Match the stdlib's IsPrivate logic.
 	if ip.Is4() {
 		// RFC 1918 allocates 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 as
diff --git a/src/net/netip/netip_test.go b/src/net/netip/netip_test.go
index 0f80bb0ab0e56..5c7ad14c5c702 100644
--- a/src/net/netip/netip_test.go
+++ b/src/net/netip/netip_test.go
@@ -589,10 +589,13 @@ func TestIPProperties(t *testing.T) {
 		ilm6     = mustIP("ff01::1")
 		ilmZone6 = mustIP("ff01::1%eth0")
 
-		private4a = mustIP("10.0.0.1")
-		private4b = mustIP("172.16.0.1")
-		private4c = mustIP("192.168.1.1")
-		private6  = mustIP("fd00::1")
+		private4a        = mustIP("10.0.0.1")
+		private4b        = mustIP("172.16.0.1")
+		private4c        = mustIP("192.168.1.1")
+		private6         = mustIP("fd00::1")
+		private6mapped4a = mustIP("::ffff:10.0.0.1")
+		private6mapped4b = mustIP("::ffff:172.16.0.1")
+		private6mapped4c = mustIP("::ffff:192.168.1.1")
 	)
 
 	tests := []struct {
@@ -616,6 +619,11 @@ func TestIPProperties(t *testing.T) {
 			ip:            unicast4,
 			globalUnicast: true,
 		},
+		{
+			name:          "unicast v6 mapped v4Addr",
+			ip:            AddrFrom16(unicast4.As16()),
+			globalUnicast: true,
+		},
 		{
 			name:          "unicast v6Addr",
 			ip:            unicast6,
@@ -637,6 +645,12 @@ func TestIPProperties(t *testing.T) {
 			linkLocalMulticast: true,
 			multicast:          true,
 		},
+		{
+			name:               "multicast v6 mapped v4Addr",
+			ip:                 AddrFrom16(multicast4.As16()),
+			linkLocalMulticast: true,
+			multicast:          true,
+		},
 		{
 			name:               "multicast v6Addr",
 			ip:                 multicast6,
@@ -654,6 +668,11 @@ func TestIPProperties(t *testing.T) {
 			ip:               llu4,
 			linkLocalUnicast: true,
 		},
+		{
+			name:             "link-local unicast v6 mapped v4Addr",
+			ip:               AddrFrom16(llu4.As16()),
+			linkLocalUnicast: true,
+		},
 		{
 			name:             "link-local unicast v6Addr",
 			ip:               llu6,
@@ -679,6 +698,11 @@ func TestIPProperties(t *testing.T) {
 			ip:       IPv6Loopback(),
 			loopback: true,
 		},
+		{
+			name:     "loopback v6 mapped v4Addr",
+			ip:       AddrFrom16(IPv6Loopback().As16()),
+			loopback: true,
+		},
 		{
 			name:                    "interface-local multicast v6Addr",
 			ip:                      ilm6,
@@ -715,6 +739,24 @@ func TestIPProperties(t *testing.T) {
 			globalUnicast: true,
 			private:       true,
 		},
+		{
+			name:          "private v6 mapped v4Addr 10/8",
+			ip:            private6mapped4a,
+			globalUnicast: true,
+			private:       true,
+		},
+		{
+			name:          "private v6 mapped v4Addr 172.16/12",
+			ip:            private6mapped4b,
+			globalUnicast: true,
+			private:       true,
+		},
+		{
+			name:          "private v6 mapped v4Addr 192.168/16",
+			ip:            private6mapped4c,
+			globalUnicast: true,
+			private:       true,
+		},
 		{
 			name:        "unspecified v4Addr",
 			ip:          IPv4Unspecified(),
