From 9109b7679e13aa34a54834cfb4949cac4b96e576 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Sun, 1 Sep 2019 17:21:42 -0700 Subject: [PATCH] unix: add IoctlRetInt for Linux Add IoctlRetInt() for ioctls that use syscall's return value as an output parameter. Add a unit test for Linux, and the NS_GET_ defines that it requires (see ioctl_ns(2)). Motivation: Currently, x/sys/unix provides a few functions to deal with ioctls. In particular, IoctlGetInt(), which "performs an ioctl operation which gets an integer value". It does that by passing a pointer to an integer to a syscall and returning that integer. The value returned from syscall is treated as success/failure flag (0 means success, -1 means failure, and in such case errno is used to figure out the underlying error). It appears that there are a few ioctls in Linux and at least one in Solaris, which do not use the above way to return an int, instead they use the syscall's return value (in case it's not negative). As Linux ioctl(2) man page says, > RETURN VALUE > Usually, on success zero is returned. A few ioctl() requests > use the return value as an output parameter and return > a nonnegative value on success. On error, -1 is returned, > and errno is set appropriately. Currently I am aware of at least 6 Linux ioctls that do that (return the value directly): * LOOP_CTL_* ioctls on /dev/loop-control (all 3 of them). Source: loop(4) * NS_* ioctls (3 out of 4) on /proc/PID/ns/*. Source: ioctl_ns(2) And one in Solaris: * I_FIND ioctl, source: streamio(7i). There might be some more ioctls like the ones above, but since ioctls are scarcely documented, it is hard to say how many more. Obviously, using IoctlGetInt() for such ioctls would be a big mistake, as 0 will always be returned. For example, there was a bug in Docker's pkg/loopback (moby/moby#39801). [v6: make it linux-specific for now] Fixes golang/go#33966 Change-Id: Ie64f72fd84101b955ba14519a357e06a66d685d0 Reviewed-on: https://go-review.googlesource.com/c/sys/+/192780 Run-TryBot: Tobias Klauser TryBot-Result: Gobot Gobot Reviewed-by: Tobias Klauser --- unix/ioctl.go | 3 +++ unix/mkerrors.sh | 2 ++ unix/syscall_linux.go | 11 +++++++++++ unix/syscall_linux_test.go | 19 +++++++++++++++++++ unix/zerrors_linux_386.go | 4 ++++ unix/zerrors_linux_amd64.go | 4 ++++ unix/zerrors_linux_arm.go | 4 ++++ unix/zerrors_linux_arm64.go | 4 ++++ unix/zerrors_linux_mips.go | 4 ++++ unix/zerrors_linux_mips64.go | 4 ++++ unix/zerrors_linux_mips64le.go | 4 ++++ unix/zerrors_linux_mipsle.go | 4 ++++ unix/zerrors_linux_ppc64.go | 4 ++++ unix/zerrors_linux_ppc64le.go | 4 ++++ unix/zerrors_linux_riscv64.go | 4 ++++ unix/zerrors_linux_s390x.go | 4 ++++ unix/zerrors_linux_sparc64.go | 4 ++++ 17 files changed, 87 insertions(+) diff --git a/unix/ioctl.go b/unix/ioctl.go index 6502a438..3559e5dc 100644 --- a/unix/ioctl.go +++ b/unix/ioctl.go @@ -43,6 +43,9 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error { // IoctlGetInt performs an ioctl operation which gets an integer value // from fd, using the specified request number. +// +// A few ioctl requests use the return value as an output parameter; +// for those, IoctlRetInt should be used instead of this function. func IoctlGetInt(fd int, req uint) (int, error) { var value int err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) diff --git a/unix/mkerrors.sh b/unix/mkerrors.sh index 14624b95..85cfbd04 100755 --- a/unix/mkerrors.sh +++ b/unix/mkerrors.sh @@ -206,6 +206,7 @@ struct ltchars { #include #include #include +#include #include #include #include @@ -451,6 +452,7 @@ ccflags="$@" $2 ~ /^SYSCTL_VERS/ || $2 !~ "MNT_BITS" && $2 ~ /^(MS|MNT|UMOUNT)_/ || + $2 ~ /^NS_GET_/ || $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || $2 ~ /^(O|F|[ES]?FD|NAME|S|PTRACE|PT)_/ || $2 ~ /^KEXEC_/ || diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go index 46493985..fe30b954 100644 --- a/unix/syscall_linux.go +++ b/unix/syscall_linux.go @@ -71,6 +71,17 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { // ioctl itself should not be exposed directly, but additional get/set // functions for specific types are permissible. +// IoctlRetInt performs an ioctl operation specified by req on a device +// associated with opened file descriptor fd, and returns a non-negative +// integer that is returned by the ioctl syscall. +func IoctlRetInt(fd int, req uint) (int, error) { + ret, _, err := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), 0) + if err != 0 { + return 0, err + } + return int(ret), nil +} + // IoctlSetPointerInt performs an ioctl operation which sets an // integer value on fd, using the specified request number. The ioctl // argument is called with a pointer to the integer value, rather than diff --git a/unix/syscall_linux_test.go b/unix/syscall_linux_test.go index 6ceeb426..634e27db 100644 --- a/unix/syscall_linux_test.go +++ b/unix/syscall_linux_test.go @@ -38,6 +38,25 @@ func TestIoctlGetInt(t *testing.T) { t.Logf("%d bits of entropy available", v) } +func TestIoctlRetInt(t *testing.T) { + f, err := os.Open("/proc/self/ns/mnt") + if err != nil { + t.Skipf("skipping test, %v", err) + } + defer f.Close() + + v, err := unix.IoctlRetInt(int(f.Fd()), unix.NS_GET_NSTYPE) + if err != nil { + if err == unix.ENOTTY { + t.Skipf("old kernel? (need Linux >= 4.11)") + } + t.Fatalf("failed to perform ioctl: %v", err) + } + if v != unix.CLONE_NEWNS { + t.Fatalf("unexpected return from ioctl; expected %v, got %v", v, unix.CLONE_NEWNS) + } +} + func TestIoctlGetRTCTime(t *testing.T) { f, err := os.Open("/dev/rtc0") if err != nil { diff --git a/unix/zerrors_linux_386.go b/unix/zerrors_linux_386.go index 3fb475bc..2839b3df 100644 --- a/unix/zerrors_linux_386.go +++ b/unix/zerrors_linux_386.go @@ -1408,6 +1408,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0xb703 + NS_GET_OWNER_UID = 0xb704 + NS_GET_PARENT = 0xb702 + NS_GET_USERNS = 0xb701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_amd64.go b/unix/zerrors_linux_amd64.go index 9c4e19f9..99e3a3de 100644 --- a/unix/zerrors_linux_amd64.go +++ b/unix/zerrors_linux_amd64.go @@ -1408,6 +1408,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0xb703 + NS_GET_OWNER_UID = 0xb704 + NS_GET_PARENT = 0xb702 + NS_GET_USERNS = 0xb701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_arm.go b/unix/zerrors_linux_arm.go index a1f038c0..f5f5ee56 100644 --- a/unix/zerrors_linux_arm.go +++ b/unix/zerrors_linux_arm.go @@ -1406,6 +1406,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0xb703 + NS_GET_OWNER_UID = 0xb704 + NS_GET_PARENT = 0xb702 + NS_GET_USERNS = 0xb701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_arm64.go b/unix/zerrors_linux_arm64.go index 504ce138..64573bdb 100644 --- a/unix/zerrors_linux_arm64.go +++ b/unix/zerrors_linux_arm64.go @@ -1409,6 +1409,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0xb703 + NS_GET_OWNER_UID = 0xb704 + NS_GET_PARENT = 0xb702 + NS_GET_USERNS = 0xb701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_mips.go b/unix/zerrors_linux_mips.go index 58b64290..3e948be3 100644 --- a/unix/zerrors_linux_mips.go +++ b/unix/zerrors_linux_mips.go @@ -1406,6 +1406,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0x2000b703 + NS_GET_OWNER_UID = 0x2000b704 + NS_GET_PARENT = 0x2000b702 + NS_GET_USERNS = 0x2000b701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_mips64.go b/unix/zerrors_linux_mips64.go index 35e33de6..8ac128bb 100644 --- a/unix/zerrors_linux_mips64.go +++ b/unix/zerrors_linux_mips64.go @@ -1406,6 +1406,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0x2000b703 + NS_GET_OWNER_UID = 0x2000b704 + NS_GET_PARENT = 0x2000b702 + NS_GET_USERNS = 0x2000b701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_mips64le.go b/unix/zerrors_linux_mips64le.go index 574fcd8c..e8845a7d 100644 --- a/unix/zerrors_linux_mips64le.go +++ b/unix/zerrors_linux_mips64le.go @@ -1406,6 +1406,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0x2000b703 + NS_GET_OWNER_UID = 0x2000b704 + NS_GET_PARENT = 0x2000b702 + NS_GET_USERNS = 0x2000b701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_mipsle.go b/unix/zerrors_linux_mipsle.go index cdf0cf5f..338c044e 100644 --- a/unix/zerrors_linux_mipsle.go +++ b/unix/zerrors_linux_mipsle.go @@ -1406,6 +1406,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0x2000b703 + NS_GET_OWNER_UID = 0x2000b704 + NS_GET_PARENT = 0x2000b702 + NS_GET_USERNS = 0x2000b701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_ppc64.go b/unix/zerrors_linux_ppc64.go index eefdb328..a696532f 100644 --- a/unix/zerrors_linux_ppc64.go +++ b/unix/zerrors_linux_ppc64.go @@ -1407,6 +1407,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80000000 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0x2000b703 + NS_GET_OWNER_UID = 0x2000b704 + NS_GET_PARENT = 0x2000b702 + NS_GET_USERNS = 0x2000b701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_ppc64le.go b/unix/zerrors_linux_ppc64le.go index 78db2104..9197b335 100644 --- a/unix/zerrors_linux_ppc64le.go +++ b/unix/zerrors_linux_ppc64le.go @@ -1407,6 +1407,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80000000 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0x2000b703 + NS_GET_OWNER_UID = 0x2000b704 + NS_GET_PARENT = 0x2000b702 + NS_GET_USERNS = 0x2000b701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_riscv64.go b/unix/zerrors_linux_riscv64.go index 0cd07f93..d1e023ed 100644 --- a/unix/zerrors_linux_riscv64.go +++ b/unix/zerrors_linux_riscv64.go @@ -1406,6 +1406,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0xb703 + NS_GET_OWNER_UID = 0xb704 + NS_GET_PARENT = 0xb702 + NS_GET_USERNS = 0xb701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_s390x.go b/unix/zerrors_linux_s390x.go index ac4f1d9f..1dfacf18 100644 --- a/unix/zerrors_linux_s390x.go +++ b/unix/zerrors_linux_s390x.go @@ -1406,6 +1406,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0xb703 + NS_GET_OWNER_UID = 0xb704 + NS_GET_PARENT = 0xb702 + NS_GET_USERNS = 0xb701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80 diff --git a/unix/zerrors_linux_sparc64.go b/unix/zerrors_linux_sparc64.go index 8a12f141..b78e49fc 100644 --- a/unix/zerrors_linux_sparc64.go +++ b/unix/zerrors_linux_sparc64.go @@ -1410,6 +1410,10 @@ const ( NLM_F_ROOT = 0x100 NOFLSH = 0x80 NSFS_MAGIC = 0x6e736673 + NS_GET_NSTYPE = 0x2000b703 + NS_GET_OWNER_UID = 0x2000b704 + NS_GET_PARENT = 0x2000b702 + NS_GET_USERNS = 0x2000b701 OCFS2_SUPER_MAGIC = 0x7461636f OCRNL = 0x8 OFDEL = 0x80