unix: generate ioctlPtr on Linux with unsafe.Pointer arg

The existing ioctl stubs for all UNIX-like platforms take a value of type
uintptr for the arg parameter. However, arguments which are cast from
unsafe.Pointer to uintptr technically violate the rules for package unsafe.
unsafe only allows a conversion from unsafe.Pointer to uintptr directly
within a call to Syscall.

ioctl is used on all UNIX-like operating systems and each one will have
to be updated accordingly where pointer arguments are passed to system
calls. To remedy this on Linux, we generate a new function called
ioctlPtr which takes a value of type unsafe.Pointer for arg. More
operating systems can be updated in future CLs by folks who have access
to those systems and can run the appropriate code generator.

Updates golang/go#44834

Change-Id: Ia9424be424b3dba91bb44d3a7a12bfb2179f0d86
Reviewed-on: https://go-review.googlesource.com/c/sys/+/340915
Trust: Matt Layher <mdlayher@gmail.com>
Trust: Bryan C. Mills <bcmills@google.com>
Run-TryBot: Matt Layher <mdlayher@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
diff --git a/unix/ioctl_linux.go b/unix/ioctl_linux.go
index 7e4f64a..d368ad2 100644
--- a/unix/ioctl_linux.go
+++ b/unix/ioctl_linux.go
@@ -22,30 +22,30 @@
 
 func IoctlGetUint32(fd int, req uint) (uint32, error) {
 	var value uint32
-	err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
+	err := ioctlPtr(fd, req, unsafe.Pointer(&value))
 	return value, err
 }
 
 func IoctlGetRTCTime(fd int) (*RTCTime, error) {
 	var value RTCTime
-	err := ioctl(fd, RTC_RD_TIME, uintptr(unsafe.Pointer(&value)))
+	err := ioctlPtr(fd, RTC_RD_TIME, unsafe.Pointer(&value))
 	return &value, err
 }
 
 func IoctlSetRTCTime(fd int, value *RTCTime) error {
-	err := ioctl(fd, RTC_SET_TIME, uintptr(unsafe.Pointer(value)))
+	err := ioctlPtr(fd, RTC_SET_TIME, unsafe.Pointer(value))
 	runtime.KeepAlive(value)
 	return err
 }
 
 func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) {
 	var value RTCWkAlrm
-	err := ioctl(fd, RTC_WKALM_RD, uintptr(unsafe.Pointer(&value)))
+	err := ioctlPtr(fd, RTC_WKALM_RD, unsafe.Pointer(&value))
 	return &value, err
 }
 
 func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error {
-	err := ioctl(fd, RTC_WKALM_SET, uintptr(unsafe.Pointer(value)))
+	err := ioctlPtr(fd, RTC_WKALM_SET, unsafe.Pointer(value))
 	runtime.KeepAlive(value)
 	return err
 }
@@ -61,7 +61,7 @@
 	value := EthtoolDrvinfo{Cmd: ETHTOOL_GDRVINFO}
 	ifrd := ifr.SetData(unsafe.Pointer(&value))
 
-	err = ioctl(fd, SIOCETHTOOL, uintptr(unsafe.Pointer(&ifrd)))
+	err = ioctlPtr(fd, SIOCETHTOOL, unsafe.Pointer(&ifrd))
 	runtime.KeepAlive(ifrd)
 	return &value, err
 }
@@ -71,7 +71,7 @@
 // https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html.
 func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) {
 	var value WatchdogInfo
-	err := ioctl(fd, WDIOC_GETSUPPORT, uintptr(unsafe.Pointer(&value)))
+	err := ioctlPtr(fd, WDIOC_GETSUPPORT, unsafe.Pointer(&value))
 	return &value, err
 }
 
@@ -79,6 +79,7 @@
 // more information, see:
 // https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html.
 func IoctlWatchdogKeepalive(fd int) error {
+	// arg is ignored and not a pointer, so ioctl is fine instead of ioctlPtr.
 	return ioctl(fd, WDIOC_KEEPALIVE, 0)
 }
 
@@ -86,7 +87,7 @@
 // range of data conveyed in value to the file associated with the file
 // descriptor destFd. See the ioctl_ficlonerange(2) man page for details.
 func IoctlFileCloneRange(destFd int, value *FileCloneRange) error {
-	err := ioctl(destFd, FICLONERANGE, uintptr(unsafe.Pointer(value)))
+	err := ioctlPtr(destFd, FICLONERANGE, unsafe.Pointer(value))
 	runtime.KeepAlive(value)
 	return err
 }
@@ -139,7 +140,7 @@
 		rawinfo.Reserved = value.Info[i].Reserved
 	}
 
-	err := ioctl(srcFd, FIDEDUPERANGE, uintptr(unsafe.Pointer(&buf[0])))
+	err := ioctlPtr(srcFd, FIDEDUPERANGE, unsafe.Pointer(&buf[0]))
 
 	// Output
 	for i := range value.Info {
@@ -157,31 +158,31 @@
 }
 
 func IoctlHIDGetDesc(fd int, value *HIDRawReportDescriptor) error {
-	err := ioctl(fd, HIDIOCGRDESC, uintptr(unsafe.Pointer(value)))
+	err := ioctlPtr(fd, HIDIOCGRDESC, unsafe.Pointer(value))
 	runtime.KeepAlive(value)
 	return err
 }
 
 func IoctlHIDGetRawInfo(fd int) (*HIDRawDevInfo, error) {
 	var value HIDRawDevInfo
-	err := ioctl(fd, HIDIOCGRAWINFO, uintptr(unsafe.Pointer(&value)))
+	err := ioctlPtr(fd, HIDIOCGRAWINFO, unsafe.Pointer(&value))
 	return &value, err
 }
 
 func IoctlHIDGetRawName(fd int) (string, error) {
 	var value [_HIDIOCGRAWNAME_LEN]byte
-	err := ioctl(fd, _HIDIOCGRAWNAME, uintptr(unsafe.Pointer(&value[0])))
+	err := ioctlPtr(fd, _HIDIOCGRAWNAME, unsafe.Pointer(&value[0]))
 	return ByteSliceToString(value[:]), err
 }
 
 func IoctlHIDGetRawPhys(fd int) (string, error) {
 	var value [_HIDIOCGRAWPHYS_LEN]byte
-	err := ioctl(fd, _HIDIOCGRAWPHYS, uintptr(unsafe.Pointer(&value[0])))
+	err := ioctlPtr(fd, _HIDIOCGRAWPHYS, unsafe.Pointer(&value[0]))
 	return ByteSliceToString(value[:]), err
 }
 
 func IoctlHIDGetRawUniq(fd int) (string, error) {
 	var value [_HIDIOCGRAWUNIQ_LEN]byte
-	err := ioctl(fd, _HIDIOCGRAWUNIQ, uintptr(unsafe.Pointer(&value[0])))
+	err := ioctlPtr(fd, _HIDIOCGRAWUNIQ, unsafe.Pointer(&value[0]))
 	return ByteSliceToString(value[:]), err
 }
diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index 41b91fd..77522ea 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -66,11 +66,18 @@
 	return fchmodat(dirfd, path, mode)
 }
 
-//sys	ioctl(fd int, req uint, arg uintptr) (err error)
+//sys	ioctl(fd int, req uint, arg uintptr) (err error) = SYS_IOCTL
+//sys	ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL
 
-// ioctl itself should not be exposed directly, but additional get/set
-// functions for specific types are permissible.
-// These are defined in ioctl.go and ioctl_linux.go.
+// ioctl itself should not be exposed directly, but additional get/set functions
+// for specific types are permissible. These are defined in ioctl.go and
+// ioctl_linux.go.
+//
+// The third argument to ioctl is often a pointer but sometimes an integer.
+// Callers should use ioctlPtr when the third argument is a pointer and ioctl
+// when the third argument is an integer.
+//
+// TODO: some existing code incorrectly uses ioctl when it should use ioctlPtr.
 
 //sys	Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error)
 
diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go
index 7305cc9..d566632 100644
--- a/unix/zsyscall_linux.go
+++ b/unix/zsyscall_linux.go
@@ -48,6 +48,16 @@
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) {
+	_, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
+	if e1 != 0 {
+		err = errnoErr(e1)
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) {
 	var _p0 *byte
 	_p0, err = BytePtrFromString(oldpath)