Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sigsetsize argument to epoll_pwait is incorrect #12715

Open
gpanders opened this issue Sep 1, 2022 · 1 comment
Open

sigsetsize argument to epoll_pwait is incorrect #12715

gpanders opened this issue Sep 1, 2022 · 1 comment
Labels
bug Observed behavior contradicts documented or intended behavior os-linux standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@gpanders
Copy link
Contributor

gpanders commented Sep 1, 2022

Zig Version

0.10.0-dev.3840+2b92c5a23

Steps to Reproduce

Call std.os.linux.epoll_pwait with a non-null sigmask argument.

Complete working example:

const std = @import("std");

pub fn main() anyerror!void {
    var sock = std.net.StreamServer.init(.{});
    defer sock.deinit();

    try sock.listen(try std.net.Address.resolveIp("0.0.0.0", 5151));

    const epfd = try std.os.epoll_create1(0);
    defer std.os.close(epfd);

    var sigmask = std.os.empty_sigset;
    try std.os.epoll_ctl(epfd, std.os.linux.EPOLL.CTL_ADD, sock.sockfd.?, &std.os.linux.epoll_event{
        .events = std.os.linux.EPOLL.IN,
        .data = .{ .fd = sock.sockfd.? },
    });

    var events: [1]std.os.linux.epoll_event = undefined;
    const rc = std.os.linux.epoll_pwait(epfd, &events, 1, -1, &sigmask);
    switch (std.os.errno(rc)) {
        .SUCCESS => std.debug.print("epoll_pwait succeeded\n", .{}),
        .INVAL => std.debug.print("EINVAL\n", .{}),
        else => unreachable,
    }
}

Compile with zig build-exe and run.

Expected Behavior

It should print epoll_pwait succeeded.

Actual Behavior

It prints EINVAL.

Zig's implementation of epoll_pwait is here:

zig/lib/std/os/linux.zig

Lines 1436 to 1446 in 36f4f32

pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*const sigset_t) usize {
return syscall6(
.epoll_pwait,
@bitCast(usize, @as(isize, epoll_fd)),
@ptrToInt(events),
@intCast(usize, maxevents),
@bitCast(usize, @as(isize, timeout)),
@ptrToInt(sigmask),
@sizeOf(sigset_t),
);
}

It passes @sizeOf(sigset_t) as the final argument of the syscall, which matches what the man page for epoll_pwait says:

C library/kernel differences
The raw epoll_pwait() and epoll_pwait2() system calls have a sixth argument, size_t sigsetsize, which specifies the size in bytes of the sigmask argument. The glibc epoll_pwait() wrapper function specifies this argument as a fixed value (equal to sizeof(sigset_t)).

Also according to the man page, the only occasion that EINVAL should be returned is when epfd is not an epoll file descriptor or maxevents is less than or equal to zero. But neither of those cases are true in this example. But if you check the Linux kernel sources, you find there is one more case where EINVAL can be returned, and that is if the sigsetsize argument does not match the size of the sigset_t data structure in the kernel:

if (sigsetsize != sizeof(sigset_t))
		return -EINVAL;

The sigset_t data structure defined in the kernel is 8 bytes (for x86-64):

typedef struct {
	unsigned long sig[_NSIG_WORDS];
} sigset_t;

I also checked the glibc sources to see what they are actually doing. They use a macro __NSIG_BYTES defined as:

#define __NSIG_BYTES (__NSIG_WORDS * (ULONG_WIDTH / UCHAR_WIDTH))

which, when you resolve all of the macros, is also 8 bytes for x86-64.

The sigset_t that Zig is passing to @sizeOf is 128 bytes. This size mismatch causes the EINVAL error. It looks like Zig needs to define a separate constant for this sigsetsize argument rather than using @sizeOf(sigset_t) (as the epoll_pwait man page would have you believe).

@gpanders gpanders added the bug Observed behavior contradicts documented or intended behavior label Sep 1, 2022
@Vexu Vexu added standard library This issue involves writing Zig code for the standard library. os-linux labels Sep 2, 2022
@Vexu Vexu added this to the 0.11.0 milestone Sep 2, 2022
@billzez
Copy link
Contributor

billzez commented Feb 18, 2023

I've hit this bug as well. Like op says, sigset_t is 128 bytes and should be 8 bytes. Also, the kernel defines NSIG as 64, but os/linux.zig has it as 65.

It appears both of these incorrect values came from the musl source code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior os-linux standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

No branches or pull requests

4 participants