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