Skip to content

Commit 3c1041b

Browse files
committed
linux: fix last argument of pselect6
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
1 parent 3fead03 commit 3c1041b

File tree

5 files changed

+75
-2
lines changed

5 files changed

+75
-2
lines changed

unix/linux/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,10 @@ const (
968968
)
969969

970970
type Sigset_t C.sigset_t
971+
type pselect6Sigset_t struct {
972+
ss *Sigset_t
973+
ssLen uintptr // Size (in bytes) of object pointed to by ss.
974+
}
971975

972976
const _C__NSIG = C._NSIG
973977

unix/syscall_linux.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1885,7 +1885,7 @@ func Getpgrp() (pid int) {
18851885
//sys PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error)
18861886
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
18871887
//sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error)
1888-
//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) = SYS_PSELECT6
1888+
//sys pselect6(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *pselect6Sigset_t) (n int, err error)
18891889
//sys read(fd int, p []byte) (n int, err error)
18901890
//sys Removexattr(path string, attr string) (err error)
18911891
//sys Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error)
@@ -2438,6 +2438,36 @@ func Getresgid() (rgid, egid, sgid int) {
24382438
return int(r), int(e), int(s)
24392439
}
24402440

2441+
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
2442+
// Per https://man7.org/linux/man-pages/man2/select.2.html#NOTES,
2443+
// The Linux pselect6() system call modifies its timeout argument.
2444+
// [Not modifying the argument] is the behavior required by POSIX.1-2001.
2445+
var mutableTimeout *Timespec
2446+
if timeout != nil {
2447+
mutableTimeout = new(Timespec)
2448+
*mutableTimeout = *timeout
2449+
}
2450+
2451+
// The final argument of the pselect6() system call is not a
2452+
// sigset_t * pointer, but is instead a structure
2453+
var kernelMask *pselect6Sigset_t
2454+
if sigmask != nil {
2455+
wordBits := 32 << (^uintptr(0) >> 63)
2456+
sigsetWords := (_C__NSIG - 1 + wordBits - 1) / (wordBits)
2457+
2458+
// A sigset stores one bit per signal,
2459+
// offset by 1 (because signal 0 does not exist).
2460+
// So the number of words needed is ⌈__C_NSIG - 1 / wordBits⌉.
2461+
sigsetBytes := sigsetWords * (wordBits / 8)
2462+
kernelMask = &pselect6Sigset_t{
2463+
ss: sigmask,
2464+
ssLen: uintptr(sigsetBytes),
2465+
}
2466+
}
2467+
2468+
return pselect6(nfd, r, w, e, mutableTimeout, kernelMask)
2469+
}
2470+
24412471
/*
24422472
* Unimplemented
24432473
*/

unix/syscall_linux_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,40 @@ func TestPselect(t *testing.T) {
444444
}
445445
}
446446

447+
func TestPselectWithSigmask(t *testing.T) {
448+
var sigmask unix.Sigset_t
449+
sigmask.Val[0] |= 1 << (uint(unix.SIGUSR1) - 1)
450+
for {
451+
n, err := unix.Pselect(0, nil, nil, nil, &unix.Timespec{Sec: 0, Nsec: 0}, &sigmask)
452+
if err == unix.EINTR {
453+
t.Logf("Pselect interrupted")
454+
continue
455+
} else if err != nil {
456+
t.Fatalf("Pselect: %v", err)
457+
}
458+
if n != 0 {
459+
t.Fatalf("Pselect: got %v ready file descriptors, expected 0", n)
460+
}
461+
break
462+
}
463+
464+
for {
465+
var sigmask unix.Sigset_t
466+
sigmask.Val[0] |= 1 << (uint(unix.SIGUSR1) - 1)
467+
n, err := unix.Pselect(0, nil, nil, nil, &unix.Timespec{Sec: 0, Nsec: 0}, &sigmask)
468+
if err == unix.EINTR {
469+
t.Logf("Pselect interrupted")
470+
continue
471+
} else if err != nil {
472+
t.Fatalf("Pselect: %v", err)
473+
}
474+
if n != 0 {
475+
t.Fatalf("Pselect: got %v ready file descriptors, expected 0", n)
476+
}
477+
break
478+
}
479+
}
480+
447481
func TestSchedSetaffinity(t *testing.T) {
448482
var newMask unix.CPUSet
449483
newMask.Zero()

unix/zsyscall_linux.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

unix/ztypes_linux.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)