all: fix most of the remaining windows -d=checkptr violations

This change replaces

buf := [HUGE_CONST]*T)(unsafe.Pointer(p))[:]

with

buf := [HUGE_CONST]*T)(unsafe.Pointer(p))[:n:n]

Pointer p points to n of T elements. New unsafe pointer conversion
logic verifies that both first and last elements point into the same
Go variable.

This change replaces [:] with [:n:n] to please pointer checker.
According to @mdempsky, compiler specially recognizes when you
combine a pointer conversion with a full slice operation in a single
expression and makes an exception.

After this, only one failure in net remains when running:

go test -a -short -gcflags=all=-d=checkptr std cmd

Updates #34972

Change-Id: I2c8731650c856264bc788e4e07fa0530f7c250fa
Reviewed-on: https://go-review.googlesource.com/c/go/+/208617
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 753a793..fd256ee 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -151,7 +151,7 @@
 	"syscall/js":                        {"L0"},
 	"internal/oserror":                  {"L0"},
 	"internal/syscall/unix":             {"L0", "syscall"},
-	"internal/syscall/windows":          {"L0", "syscall", "internal/syscall/windows/sysdll"},
+	"internal/syscall/windows":          {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"},
 	"internal/syscall/windows/registry": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"},
 	"time": {
 		// "L0" without the "io" package:
diff --git a/src/internal/syscall/windows/reparse_windows.go b/src/internal/syscall/windows/reparse_windows.go
index 610b733..6e11139 100644
--- a/src/internal/syscall/windows/reparse_windows.go
+++ b/src/internal/syscall/windows/reparse_windows.go
@@ -60,8 +60,9 @@
 
 // Path returns path stored in rb.
 func (rb *SymbolicLinkReparseBuffer) Path() string {
-	p := (*[0xffff]uint16)(unsafe.Pointer(&rb.PathBuffer[0]))
-	return syscall.UTF16ToString(p[rb.SubstituteNameOffset/2 : (rb.SubstituteNameOffset+rb.SubstituteNameLength)/2])
+	n1 := rb.SubstituteNameOffset / 2
+	n2 := (rb.SubstituteNameOffset + rb.SubstituteNameLength) / 2
+	return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(&rb.PathBuffer[0]))[n1:n2:n2])
 }
 
 type MountPointReparseBuffer struct {
@@ -83,6 +84,7 @@
 
 // Path returns path stored in rb.
 func (rb *MountPointReparseBuffer) Path() string {
-	p := (*[0xffff]uint16)(unsafe.Pointer(&rb.PathBuffer[0]))
-	return syscall.UTF16ToString(p[rb.SubstituteNameOffset/2 : (rb.SubstituteNameOffset+rb.SubstituteNameLength)/2])
+	n1 := rb.SubstituteNameOffset / 2
+	n2 := (rb.SubstituteNameOffset + rb.SubstituteNameLength) / 2
+	return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(&rb.PathBuffer[0]))[n1:n2:n2])
 }
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index 099e91e..dc64111 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -7,9 +7,29 @@
 import (
 	"sync"
 	"syscall"
+	"unicode/utf16"
 	"unsafe"
 )
 
+// UTF16PtrToString is like UTF16ToString, but takes *uint16
+// as a parameter instead of []uint16.
+// max is how many times p can be advanced looking for the null terminator.
+// If max is hit, the string is truncated at that point.
+func UTF16PtrToString(p *uint16, max int) string {
+	if p == nil {
+		return ""
+	}
+	// Find NUL terminator.
+	end := unsafe.Pointer(p)
+	n := 0
+	for *(*uint16)(end) != 0 && n < max {
+		end = unsafe.Pointer(uintptr(end) + unsafe.Sizeof(*p))
+		n++
+	}
+	s := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:n:n]
+	return string(utf16.Decode(s))
+}
+
 const (
 	ERROR_SHARING_VIOLATION      syscall.Errno = 32
 	ERROR_LOCK_VIOLATION         syscall.Errno = 33
diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go
index 28b0a65..5449432 100644
--- a/src/net/interface_windows.go
+++ b/src/net/interface_windows.go
@@ -58,7 +58,7 @@
 		if ifindex == 0 || ifindex == int(index) {
 			ifi := Interface{
 				Index: int(index),
-				Name:  syscall.UTF16ToString((*(*[10000]uint16)(unsafe.Pointer(aa.FriendlyName)))[:]),
+				Name:  windows.UTF16PtrToString(aa.FriendlyName, 10000),
 			}
 			if aa.OperStatus == windows.IfOperStatusUp {
 				ifi.Flags |= FlagUp
diff --git a/src/net/lookup_windows.go b/src/net/lookup_windows.go
index cb840ae..7d5c941 100644
--- a/src/net/lookup_windows.go
+++ b/src/net/lookup_windows.go
@@ -6,6 +6,7 @@
 
 import (
 	"context"
+	"internal/syscall/windows"
 	"os"
 	"runtime"
 	"syscall"
@@ -233,7 +234,7 @@
 	defer syscall.DnsRecordListFree(r, 1)
 
 	resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), r)
-	cname := syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(resolved))[:])
+	cname := windows.UTF16PtrToString(resolved, 256)
 	return absDomainName([]byte(cname)), nil
 }
 
@@ -277,7 +278,7 @@
 	mxs := make([]*MX, 0, 10)
 	for _, p := range validRecs(r, syscall.DNS_TYPE_MX, name) {
 		v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
-		mxs = append(mxs, &MX{absDomainName([]byte(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.NameExchange))[:]))), v.Preference})
+		mxs = append(mxs, &MX{absDomainName([]byte(windows.UTF16PtrToString(v.NameExchange, 256))), v.Preference})
 	}
 	byPref(mxs).sort()
 	return mxs, nil
@@ -317,8 +318,8 @@
 	for _, p := range validRecs(r, syscall.DNS_TYPE_TEXT, name) {
 		d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
 		s := ""
-		for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount] {
-			s += syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(v))[:])
+		for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount:d.StringCount] {
+			s += windows.UTF16PtrToString(v, 1<<20)
 		}
 		txts = append(txts, s)
 	}
@@ -343,7 +344,7 @@
 	ptrs := make([]string, 0, 10)
 	for _, p := range validRecs(r, syscall.DNS_TYPE_PTR, arpa) {
 		v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
-		ptrs = append(ptrs, absDomainName([]byte(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))))
+		ptrs = append(ptrs, absDomainName([]byte(windows.UTF16PtrToString(v.Host, 256))))
 	}
 	return ptrs, nil
 }
diff --git a/src/os/env_windows.go b/src/os/env_windows.go
index e8f647e..b1b1ee4 100644
--- a/src/os/env_windows.go
+++ b/src/os/env_windows.go
@@ -23,16 +23,20 @@
 	defer windows.DestroyEnvironmentBlock(block)
 	blockp := uintptr(unsafe.Pointer(block))
 	for {
-		entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:]
-		for i, v := range entry {
-			if v == 0 {
-				entry = entry[:i]
-				break
-			}
+
+		// find NUL terminator
+		end := unsafe.Pointer(blockp)
+		for *(*uint16)(end) != 0 {
+			end = unsafe.Pointer(uintptr(end) + 2)
 		}
-		if len(entry) == 0 {
+
+		n := (uintptr(end) - uintptr(unsafe.Pointer(blockp))) / 2
+		if n == 0 {
+			// environment block ends with empty string
 			break
 		}
+
+		entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:n:n]
 		env = append(env, string(utf16.Decode(entry)))
 		blockp += 2 * (uintptr(len(entry)) + 1)
 	}
diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go
index 38293a0..10503c5 100644
--- a/src/os/exec_windows.go
+++ b/src/os/exec_windows.go
@@ -6,11 +6,11 @@
 
 import (
 	"errors"
+	"internal/syscall/windows"
 	"runtime"
 	"sync/atomic"
 	"syscall"
 	"time"
-	"unsafe"
 )
 
 func (p *Process) wait() (ps *ProcessState, err error) {
@@ -98,8 +98,7 @@
 }
 
 func init() {
-	p := syscall.GetCommandLine()
-	cmd := syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(p))[:])
+	cmd := windows.UTF16PtrToString(syscall.GetCommandLine(), 0xffff)
 	if len(cmd) == 0 {
 		arg0, _ := Executable()
 		Args = []string{arg0}
diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go
index 651fe63..8c14103 100644
--- a/src/os/os_windows_test.go
+++ b/src/os/os_windows_test.go
@@ -263,7 +263,8 @@
 	buf.SubstituteNameLength = target.substituteName.length
 	buf.PrintNameOffset = target.printName.offset
 	buf.PrintNameLength = target.printName.length
-	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
+	pbuflen := len(target.pathBuf)
+	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
 
 	var rdb _REPARSE_DATA_BUFFER
 	rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT
@@ -356,7 +357,8 @@
 	if isrelative {
 		buf.Flags = windows.SYMLINK_FLAG_RELATIVE
 	}
-	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
+	pbuflen := len(target.pathBuf)
+	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
 
 	var rdb _REPARSE_DATA_BUFFER
 	rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
@@ -714,7 +716,7 @@
 						if n > consoleSize {
 							n = consoleSize
 						}
-						n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n], s16)
+						n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n:n], s16)
 						s16 = s16[n:]
 						*read = uint32(n)
 						t.Logf("read %d -> %d", toread, *read)
diff --git a/src/os/user/lookup_windows.go b/src/os/user/lookup_windows.go
index 7499f6a..faaddd2 100644
--- a/src/os/user/lookup_windows.go
+++ b/src/os/user/lookup_windows.go
@@ -44,11 +44,7 @@
 	}
 	defer syscall.NetApiBufferFree(p)
 	i := (*syscall.UserInfo10)(unsafe.Pointer(p))
-	if i.FullName == nil {
-		return "", nil
-	}
-	name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:])
-	return name, nil
+	return windows.UTF16PtrToString(i.FullName, 1024), nil
 }
 
 func lookupFullName(domain, username, domainAndUser string) (string, error) {
@@ -165,14 +161,13 @@
 	if entriesRead == 0 {
 		return nil, fmt.Errorf("listGroupsForUsernameAndDomain: NetUserGetLocalGroups() returned an empty list for domain: %s, username: %s", domain, username)
 	}
-	entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead]
+	entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead:entriesRead]
 	var sids []string
 	for _, entry := range entries {
 		if entry.Name == nil {
 			continue
 		}
-		name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(entry.Name))[:])
-		sid, err := lookupGroupName(name)
+		sid, err := lookupGroupName(windows.UTF16PtrToString(entry.Name, 1024))
 		if err != nil {
 			return nil, err
 		}
diff --git a/src/syscall/security_windows.go b/src/syscall/security_windows.go
index db80d98..3a75759 100644
--- a/src/syscall/security_windows.go
+++ b/src/syscall/security_windows.go
@@ -163,7 +163,7 @@
 		return "", e
 	}
 	defer LocalFree((Handle)(unsafe.Pointer(s)))
-	return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]), nil
+	return utf16PtrToString(s, 256), nil
 }
 
 // Len returns the length, in bytes, of a valid security identifier sid.
diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index 992f673..950c281 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -57,6 +57,25 @@
 	return string(utf16.Decode(s))
 }
 
+// utf16PtrToString is like UTF16ToString, but takes *uint16
+// as a parameter instead of []uint16.
+// max is how many times p can be advanced looking for the null terminator.
+// If max is hit, the string is truncated at that point.
+func utf16PtrToString(p *uint16, max int) string {
+	if p == nil {
+		return ""
+	}
+	// Find NUL terminator.
+	end := unsafe.Pointer(p)
+	n := 0
+	for *(*uint16)(end) != 0 && n < max {
+		end = unsafe.Pointer(uintptr(end) + unsafe.Sizeof(*p))
+		n++
+	}
+	s := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:n:n]
+	return string(utf16.Decode(s))
+}
+
 // StringToUTF16Ptr returns pointer to the UTF-16 encoding of
 // the UTF-8 string s, with a terminating NUL added. If s
 // contains a NUL byte this function panics instead of
@@ -769,7 +788,7 @@
 		for n < len(pp.Path) && pp.Path[n] != 0 {
 			n++
 		}
-		bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
+		bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n:n]
 		sa.Name = string(bytes)
 		return sa, nil