buytenh / ivykis

GNU Lesser General Public License v2.1
24 stars 18 forks source link

ivykis aborts on FreeBSD 13.1 (`kqueue`) when a polled pipe fd gets closed #26

Open OverOrion opened 2 years ago

OverOrion commented 2 years ago

The attached reproducer code spawns two processes, one of them polls a perfectly valid pipe fd for writing using ivykis, and the child process would be responsible for reading, but for an easier reproduction, the child just closes the pipe and exits. (Reading and then closing would result in the same abort.)

https://gist.github.com/MrAnno/0a6dd9572e8a7a981996654ccd3e5e08

Running it yields the following backtrace:

[0] from 0x000000080039233a in thr_kill
[1] from 0x000000080030ac74 in raise
[2] from 0x00000008003bc109 in abort
[3] from 0x000000080024ecbb in iv_fatal+235 at iv_fatal.c:57
[4] from 0x0000000800256832 in iv_fd_kqueue_poll+1106 at iv_fd_kqueue.c:212
[5] from 0x0000000800251561 in iv_fd_poll_and_run+177 at iv_fd.c:206
[6] from 0x0000000800253a15 in iv_main+181 at iv_main_posix.c:112
[7] from 0x0000000000201de6 in main+198 at main.c:60

I believe ivykis incorrectly reports fatal errors in case a kevent() call returns EV_ERROR on the file description: https://github.com/buytenh/ivykis/blob/f1b14555fb0b5d9acfbcfaf35b1313bb28d858c2/src/iv_fd_kqueue.c#L208-L214

The epoll implementation, for example, reports these kind of errors as non-fatal using the error callback: https://github.com/buytenh/ivykis/blob/f1b14555fb0b5d9acfbcfaf35b1313bb28d858c2/src/iv_fd_epoll.c#L185-L186

buytenh commented 10 months ago

The gist.github.com link gives a 404 -- can you repost the reproducer somewhere? (Preferably verbatim in a comment in this issue.)

bazsi commented 5 months ago

@OverOrion do you still happen to have the reproducer for this?

OverOrion commented 5 months ago

Unfortunately not :(

buytenh commented 5 months ago

@MrAnno Do you still have the reproducer, given that it was posted under your gist.github.com?

MrAnno commented 5 months ago

I don't have it, but I think I remember what to do. I'll try to recreate the reproducer next week.

MrAnno commented 5 months ago
#0  0x000000080058b41a in thr_kill () from /lib/libc.so.7
#1  0x0000000800504e64 in raise () from /lib/libc.so.7
#2  0x00000008005b56f9 in abort () from /lib/libc.so.7
#3  0x0000000800448a6e in iv_fatal (fmt=<optimized out>) at iv_fatal.c:57
#4  0x000000080044fcd4 in iv_fd_kqueue_poll (st=0x800c09000, active=0x7fffffffe960, abs=0x0) at iv_fd_kqueue.c:212
#5  0x000000080044b3e6 in iv_fd_poll_and_run (st=0x800c09000, abs=0x0) at iv_fd.c:205
#6  0x000000080044cf90 in iv_main () at iv_main_posix.c:112
#7  0x0000000000400d6e in main () at test.c:55
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/wait.h>

#include <iv.h>

void fatal(const char* msg)
{
    perror(msg);
    exit(1);
}

void write_fd(void* c)
{
    struct iv_fd* fd = (struct iv_fd*) c;

    const char* buf = "hello\n";
    int rc = write(fd->fd, buf, strlen(buf) + 1);

    if (rc < 0)
        iv_fd_unregister(fd);
}

int main()
{
    iv_init();

    int pp[2];
    if (pipe(pp) < 0)
        fatal("pipe()");

    pid_t pid = fork();

    if (pid < 0)
        fatal("fork()");

    if (pid == 0) {
        close(pp[1]);
        close(pp[0]);
        return 0;
    }

    close(pp[0]);

    struct iv_fd fd;
    IV_FD_INIT(&fd);
    fd.fd = pp[1];
    fd.cookie = &fd;
    fd.handler_out = write_fd;
    iv_fd_register(&fd);

    iv_main();
    iv_deinit();

    close(pp[1]);

    int rc;
    if (wait(&rc) < 0)
        fatal("wait()");

    return 0;
}