From 25d0004552bee01a3ba08d943dadc39e3c361f77 Mon Sep 17 00:00:00 2001 From: Mauri de Souza Meneguzzo Date: Tue, 18 Jul 2023 21:23:29 +0000 Subject: [PATCH] unix: fix last argument of pselect6 on linux On Linux, the last argument of pselect6 system call is **not** a sigseg_t * pointer, but instead it is a structure of the form: struct { const sigset_t *ss; /* Pointer to signal set */ size_t ss_len; /* Size (in bytes) of object pointed }; See man 2 pselect6. Fixes #61251 Change-Id: Id0aa122a77796713bc6d624dc395d396fbc0c5e2 GitHub-Last-Rev: cb3c6d7da9b846843a4a81898ebfdcf2e449942a GitHub-Pull-Request: golang/sys#167 Reviewed-on: https://go-review.googlesource.com/c/sys/+/510195 Reviewed-by: Bryan Mills Reviewed-by: Ian Lance Taylor TryBot-Result: Gopher Robot Run-TryBot: Ian Lance Taylor Auto-Submit: Ian Lance Taylor --- unix/linux/types.go | 4 ++++ unix/syscall_linux.go | 35 ++++++++++++++++++++++++++++++++++- unix/syscall_linux_amd64.go | 2 +- unix/syscall_linux_arm64.go | 2 +- unix/syscall_linux_loong64.go | 2 +- unix/syscall_linux_mips64x.go | 2 +- unix/syscall_linux_riscv64.go | 2 +- unix/syscall_linux_test.go | 18 ++++++++++++++++++ unix/zsyscall_linux.go | 2 +- unix/ztypes_linux.go | 5 +++++ 10 files changed, 67 insertions(+), 7 deletions(-) diff --git a/unix/linux/types.go b/unix/linux/types.go index 469ff6bcf..f2cbd2d45 100644 --- a/unix/linux/types.go +++ b/unix/linux/types.go @@ -968,6 +968,10 @@ const ( ) type Sigset_t C.sigset_t +type sigset_argpack struct { + ss *Sigset_t + ssLen uintptr // Size (in bytes) of object pointed to by ss. +} const _C__NSIG = C._NSIG diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go index 332a74b8e..a730878e4 100644 --- a/unix/syscall_linux.go +++ b/unix/syscall_linux.go @@ -1885,7 +1885,7 @@ func Getpgrp() (pid int) { //sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) //sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT //sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error) -//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) = SYS_PSELECT6 +//sys pselect6(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *sigset_argpack) (n int, err error) //sys read(fd int, p []byte) (n int, err error) //sys Removexattr(path string, attr string) (err error) //sys Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error) @@ -2438,6 +2438,39 @@ func Getresgid() (rgid, egid, sgid int) { return int(r), int(e), int(s) } +// Pselect is a wrapper around the Linux pselect6 system call. +// This version does not modify the timeout argument. +func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { + // Per https://man7.org/linux/man-pages/man2/select.2.html#NOTES, + // The Linux pselect6() system call modifies its timeout argument. + // [Not modifying the argument] is the behavior required by POSIX.1-2001. + var mutableTimeout *Timespec + if timeout != nil { + mutableTimeout = new(Timespec) + *mutableTimeout = *timeout + } + + // The final argument of the pselect6() system call is not a + // sigset_t * pointer, but is instead a structure + var kernelMask *sigset_argpack + if sigmask != nil { + wordBits := 32 << (^uintptr(0) >> 63) // see math.intSize + + // A sigset stores one bit per signal, + // offset by 1 (because signal 0 does not exist). + // So the number of words needed is ⌈__C_NSIG - 1 / wordBits⌉. + sigsetWords := (_C__NSIG - 1 + wordBits - 1) / (wordBits) + + sigsetBytes := uintptr(sigsetWords * (wordBits / 8)) + kernelMask = &sigset_argpack{ + ss: sigmask, + ssLen: sigsetBytes, + } + } + + return pselect6(nfd, r, w, e, mutableTimeout, kernelMask) +} + /* * Unimplemented */ diff --git a/unix/syscall_linux_amd64.go b/unix/syscall_linux_amd64.go index 5b21fcfd7..70601ce36 100644 --- a/unix/syscall_linux_amd64.go +++ b/unix/syscall_linux_amd64.go @@ -40,7 +40,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err if timeout != nil { ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000} } - return Pselect(nfd, r, w, e, ts, nil) + return pselect6(nfd, r, w, e, ts, nil) } //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) diff --git a/unix/syscall_linux_arm64.go b/unix/syscall_linux_arm64.go index a81f5742b..f5266689a 100644 --- a/unix/syscall_linux_arm64.go +++ b/unix/syscall_linux_arm64.go @@ -33,7 +33,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err if timeout != nil { ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000} } - return Pselect(nfd, r, w, e, ts, nil) + return pselect6(nfd, r, w, e, ts, nil) } //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) diff --git a/unix/syscall_linux_loong64.go b/unix/syscall_linux_loong64.go index 69d2d7c3d..f6ab02ec1 100644 --- a/unix/syscall_linux_loong64.go +++ b/unix/syscall_linux_loong64.go @@ -28,7 +28,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err if timeout != nil { ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000} } - return Pselect(nfd, r, w, e, ts, nil) + return pselect6(nfd, r, w, e, ts, nil) } //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) diff --git a/unix/syscall_linux_mips64x.go b/unix/syscall_linux_mips64x.go index 76d564095..93fe59d25 100644 --- a/unix/syscall_linux_mips64x.go +++ b/unix/syscall_linux_mips64x.go @@ -31,7 +31,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err if timeout != nil { ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000} } - return Pselect(nfd, r, w, e, ts, nil) + return pselect6(nfd, r, w, e, ts, nil) } //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) diff --git a/unix/syscall_linux_riscv64.go b/unix/syscall_linux_riscv64.go index 35851ef70..b1de100ac 100644 --- a/unix/syscall_linux_riscv64.go +++ b/unix/syscall_linux_riscv64.go @@ -32,7 +32,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err if timeout != nil { ts = &Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000} } - return Pselect(nfd, r, w, e, ts, nil) + return pselect6(nfd, r, w, e, ts, nil) } //sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) diff --git a/unix/syscall_linux_test.go b/unix/syscall_linux_test.go index 2787fe57a..dee874598 100644 --- a/unix/syscall_linux_test.go +++ b/unix/syscall_linux_test.go @@ -444,6 +444,24 @@ func TestPselect(t *testing.T) { } } +func TestPselectWithSigmask(t *testing.T) { + var sigmask unix.Sigset_t + sigmask.Val[0] |= 1 << (uint(unix.SIGUSR1) - 1) + for { + n, err := unix.Pselect(0, nil, nil, nil, &unix.Timespec{Sec: 0, Nsec: 0}, &sigmask) + if err == unix.EINTR { + t.Logf("Pselect interrupted") + continue + } else if err != nil { + t.Fatalf("Pselect: %v", err) + } + if n != 0 { + t.Fatalf("Pselect: got %v ready file descriptors, expected 0", n) + } + break + } +} + func TestSchedSetaffinity(t *testing.T) { var newMask unix.CPUSet newMask.Zero() diff --git a/unix/zsyscall_linux.go b/unix/zsyscall_linux.go index 7ceec233f..a07321bed 100644 --- a/unix/zsyscall_linux.go +++ b/unix/zsyscall_linux.go @@ -1356,7 +1356,7 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) ( // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { +func pselect6(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *sigset_argpack) (n int, err error) { r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask))) n = int(r0) if e1 != 0 { diff --git a/unix/ztypes_linux.go b/unix/ztypes_linux.go index 02e2462c8..26ef52aaf 100644 --- a/unix/ztypes_linux.go +++ b/unix/ztypes_linux.go @@ -866,6 +866,11 @@ const ( POLLNVAL = 0x20 ) +type sigset_argpack struct { + ss *Sigset_t + ssLen uintptr +} + type SignalfdSiginfo struct { Signo uint32 Errno int32