windows: fix -d=checkptr slice failures

This CL fixes unsafe casts to slices that are missing length or capacity.

Running tests with -d=checkptr enabled may panic on casting unsafe.Pointer to a static array of large predefined length, that is most likely much bigger than the size of the actual array in memory. Checkptr check is not satisfied if slicing operator misses length and capacity arguments `(*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]`, or when there is no slicing at all `(*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))`.

To find all potential cases I used `grep -nr ")(unsafe.Pointer(" ./windows`, then filtered out safe casts when object size is always static and known at compile time.

To reproduce the issue run tests with checkptr enabled `go test -a -gcflags=all=-d=checkptr ./windows/...`.

Updates golang/go#34972
Fixes golang/go#38355

Change-Id: I9dd2084b4f9fb7618cdb140fb2f38b56b6d6cc04
GitHub-Last-Rev: 73288ad18a2692b1510f8ed564daad8d1cbd7e59
GitHub-Pull-Request: golang/sys#65
Reviewed-on: https://go-review.googlesource.com/c/sys/+/225418
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
This commit is contained in:
Yaroslav Vorobiov
2020-05-19 10:57:57 +00:00
committed by Alex Brainman
parent 1151b9dac4
commit fe76b779f2
8 changed files with 92 additions and 52 deletions
+2 -9
View File
@@ -8,7 +8,6 @@ package windows
import ( import (
"syscall" "syscall"
"unicode/utf16"
"unsafe" "unsafe"
) )
@@ -40,17 +39,11 @@ func (token Token) Environ(inheritExisting bool) (env []string, err error) {
defer DestroyEnvironmentBlock(block) defer DestroyEnvironmentBlock(block)
blockp := uintptr(unsafe.Pointer(block)) blockp := uintptr(unsafe.Pointer(block))
for { for {
entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:] entry := UTF16PtrToString((*uint16)(unsafe.Pointer(blockp)))
for i, v := range entry {
if v == 0 {
entry = entry[:i]
break
}
}
if len(entry) == 0 { if len(entry) == 0 {
break break
} }
env = append(env, string(utf16.Decode(entry))) env = append(env, entry)
blockp += 2 * (uintptr(len(entry)) + 1) blockp += 2 * (uintptr(len(entry)) + 1)
} }
return env, nil return env, nil
+15 -5
View File
@@ -7,6 +7,8 @@ package windows
import ( import (
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
) )
const ( const (
@@ -1229,7 +1231,7 @@ func (sd *SECURITY_DESCRIPTOR) String() string {
return "" return ""
} }
defer LocalFree(Handle(unsafe.Pointer(sddl))) defer LocalFree(Handle(unsafe.Pointer(sddl)))
return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(sddl))[:]) return UTF16PtrToString(sddl)
} }
// ToAbsolute converts a self-relative security descriptor into an absolute one. // ToAbsolute converts a self-relative security descriptor into an absolute one.
@@ -1307,9 +1309,17 @@ func (absoluteSD *SECURITY_DESCRIPTOR) ToSelfRelative() (selfRelativeSD *SECURIT
} }
func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR { func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR {
sdBytes := make([]byte, selfRelativeSD.Length()) sdLen := (int)(selfRelativeSD.Length())
copy(sdBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(selfRelativeSD))[:len(sdBytes)])
return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&sdBytes[0])) var src []byte
h := (*unsafeheader.Slice)(unsafe.Pointer(&src))
h.Data = unsafe.Pointer(selfRelativeSD)
h.Len = sdLen
h.Cap = sdLen
dst := make([]byte, sdLen)
copy(dst, src)
return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&dst[0]))
} }
// SecurityDescriptorFromString converts an SDDL string describing a security descriptor into a // SecurityDescriptorFromString converts an SDDL string describing a security descriptor into a
@@ -1391,6 +1401,6 @@ func ACLFromEntries(explicitEntries []EXPLICIT_ACCESS, mergedACL *ACL) (acl *ACL
} }
defer LocalFree(Handle(unsafe.Pointer(winHeapACL))) defer LocalFree(Handle(unsafe.Pointer(winHeapACL)))
aclBytes := make([]byte, winHeapACL.aclSize) aclBytes := make([]byte, winHeapACL.aclSize)
copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes)]) copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes):len(aclBytes)])
return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil
} }
+16 -24
View File
@@ -8,7 +8,6 @@ package mgr
import ( import (
"syscall" "syscall"
"unicode/utf16"
"unsafe" "unsafe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
@@ -46,28 +45,21 @@ type Config struct {
DelayedAutoStart bool // the service is started after other auto-start services are started plus a short delay DelayedAutoStart bool // the service is started after other auto-start services are started plus a short delay
} }
func toString(p *uint16) string {
if p == nil {
return ""
}
return syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(p))[:])
}
func toStringSlice(ps *uint16) []string { func toStringSlice(ps *uint16) []string {
if ps == nil {
return nil
}
r := make([]string, 0) r := make([]string, 0)
for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(ps)); true; i++ { p := unsafe.Pointer(ps)
if p[i] == 0 {
// empty string marks the end for {
if i <= from { s := windows.UTF16PtrToString((*uint16)(p))
break if len(s) == 0 {
} break
r = append(r, string(utf16.Decode(p[from:i])))
from = i + 1
} }
r = append(r, s)
offset := unsafe.Sizeof(uint16(0)) * (uintptr)(len(s)+1)
p = unsafe.Pointer(uintptr(p) + offset)
} }
return r return r
} }
@@ -110,13 +102,13 @@ func (s *Service) Config() (Config, error) {
ServiceType: p.ServiceType, ServiceType: p.ServiceType,
StartType: p.StartType, StartType: p.StartType,
ErrorControl: p.ErrorControl, ErrorControl: p.ErrorControl,
BinaryPathName: toString(p.BinaryPathName), BinaryPathName: windows.UTF16PtrToString(p.BinaryPathName),
LoadOrderGroup: toString(p.LoadOrderGroup), LoadOrderGroup: windows.UTF16PtrToString(p.LoadOrderGroup),
TagId: p.TagId, TagId: p.TagId,
Dependencies: toStringSlice(p.Dependencies), Dependencies: toStringSlice(p.Dependencies),
ServiceStartName: toString(p.ServiceStartName), ServiceStartName: windows.UTF16PtrToString(p.ServiceStartName),
DisplayName: toString(p.DisplayName), DisplayName: windows.UTF16PtrToString(p.DisplayName),
Description: toString(p2.Description), Description: windows.UTF16PtrToString(p2.Description),
DelayedAutoStart: delayedStart, DelayedAutoStart: delayedStart,
}, nil }, nil
} }
+10 -3
View File
@@ -17,6 +17,7 @@ import (
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@@ -73,7 +74,7 @@ func (m *Mgr) LockStatus() (*LockStatus, error) {
status := &LockStatus{ status := &LockStatus{
IsLocked: lockStatus.IsLocked != 0, IsLocked: lockStatus.IsLocked != 0,
Age: time.Duration(lockStatus.LockDuration) * time.Second, Age: time.Duration(lockStatus.LockDuration) * time.Second,
Owner: windows.UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(lockStatus.LockOwner))[:]), Owner: windows.UTF16PtrToString(lockStatus.LockOwner),
} }
return status, nil return status, nil
} }
@@ -201,10 +202,16 @@ func (m *Mgr) ListServices() ([]string, error) {
if servicesReturned == 0 { if servicesReturned == 0 {
return nil, nil return nil, nil
} }
services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned]
var services []windows.ENUM_SERVICE_STATUS_PROCESS
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&services))
hdr.Data = unsafe.Pointer(&buf[0])
hdr.Len = int(servicesReturned)
hdr.Cap = int(servicesReturned)
var names []string var names []string
for _, s := range services { for _, s := range services {
name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:]) name := windows.UTF16PtrToString(s.ServiceName)
names = append(names, name) names = append(names, name)
} }
return names, nil return names, nil
+9 -3
View File
@@ -12,6 +12,7 @@ import (
"time" "time"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@@ -68,8 +69,13 @@ func (s *Service) RecoveryActions() ([]RecoveryAction, error) {
return nil, err return nil, err
} }
var actions []windows.SC_ACTION
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&actions))
hdr.Data = unsafe.Pointer(p.Actions)
hdr.Len = int(p.ActionsCount)
hdr.Cap = int(p.ActionsCount)
var recoveryActions []RecoveryAction var recoveryActions []RecoveryAction
actions := (*[1024]windows.SC_ACTION)(unsafe.Pointer(p.Actions))[:p.ActionsCount]
for _, action := range actions { for _, action := range actions {
recoveryActions = append(recoveryActions, RecoveryAction{Type: int(action.Type), Delay: time.Duration(action.Delay) * time.Millisecond}) recoveryActions = append(recoveryActions, RecoveryAction{Type: int(action.Type), Delay: time.Duration(action.Delay) * time.Millisecond})
} }
@@ -112,7 +118,7 @@ func (s *Service) RebootMessage() (string, error) {
return "", err return "", err
} }
p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0])) p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0]))
return toString(p.RebootMsg), nil return windows.UTF16PtrToString(p.RebootMsg), nil
} }
// SetRecoveryCommand sets the command line of the process to execute in response to the RunCommand service controller action. // SetRecoveryCommand sets the command line of the process to execute in response to the RunCommand service controller action.
@@ -131,5 +137,5 @@ func (s *Service) RecoveryCommand() (string, error) {
return "", err return "", err
} }
p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0])) p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0]))
return toString(p.Command), nil return windows.UTF16PtrToString(p.Command), nil
} }
+2 -5
View File
@@ -7,8 +7,6 @@
package svc package svc
import ( import (
"unsafe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@@ -48,9 +46,8 @@ func IsAnInteractiveSession() (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
p := unsafe.Pointer(&gs.Groups[0])
groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount] for _, g := range gs.AllGroups() {
for _, g := range groups {
if windows.EqualSid(g.Sid, interSid) { if windows.EqualSid(g.Sid, interSid) {
return true, nil return true, nil
} }
+9 -2
View File
@@ -14,6 +14,7 @@ import (
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@@ -224,10 +225,16 @@ const (
func (s *service) run() { func (s *service) run() {
s.goWaits.Wait() s.goWaits.Wait()
s.h = windows.Handle(ssHandle) s.h = windows.Handle(ssHandle)
argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc]
var argv []*uint16
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&argv))
hdr.Data = unsafe.Pointer(sArgv)
hdr.Len = int(sArgc)
hdr.Cap = int(sArgc)
args := make([]string, len(argv)) args := make([]string, len(argv))
for i, a := range argv { for i, a := range argv {
args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:]) args[i] = windows.UTF16PtrToString(a)
} }
cmdsToHandler := make(chan ChangeRequest) cmdsToHandler := make(chan ChangeRequest)
+29 -1
View File
@@ -13,6 +13,8 @@ import (
"time" "time"
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"golang.org/x/sys/internal/unsafeheader"
) )
type Handle uintptr type Handle uintptr
@@ -117,6 +119,32 @@ func UTF16PtrFromString(s string) (*uint16, error) {
return &a[0], nil return &a[0], nil
} }
// UTF16PtrToString takes a pointer to a UTF-16 sequence and returns the corresponding UTF-8 encoded string.
// If the pointer is nil, this returns the empty string. This assumes that the UTF-16 sequence is terminated
// at a zero word; if the zero word is not present, the program may crash.
func UTF16PtrToString(p *uint16) string {
if p == nil {
return ""
}
if *p == 0 {
return ""
}
// Find NUL terminator.
n := 0
for ptr := unsafe.Pointer(p); *(*uint16)(ptr) != 0; n++ {
ptr = unsafe.Pointer(uintptr(ptr) + unsafe.Sizeof(*p))
}
var s []uint16
h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
h.Data = unsafe.Pointer(p)
h.Len = n
h.Cap = n
return string(utf16.Decode(s))
}
func Getpagesize() int { return 4096 } func Getpagesize() int { return 4096 }
// NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention. // NewCallback converts a Go function to a function pointer conforming to the stdcall calling convention.
@@ -1383,7 +1411,7 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e
return "", err return "", err
} }
defer CoTaskMemFree(unsafe.Pointer(p)) defer CoTaskMemFree(unsafe.Pointer(p))
return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil return UTF16PtrToString(p), nil
} }
// RtlGetVersion returns the version of the underlying operating system, ignoring // RtlGetVersion returns the version of the underlying operating system, ignoring