root/tests/lib/libpthread/t_compat_cancel.c
/*      $NetBSD: t_compat_cancel.c,v 1.3 2025/04/25 13:09:44 riastradh Exp $    */

/*
 * Copyright (c) 2025 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#define __LIBC12_SOURCE__       /* expose compat declarations */

#include <sys/cdefs.h>
__RCSID("$NetBSD: t_compat_cancel.c,v 1.3 2025/04/25 13:09:44 riastradh Exp $");

#include <sys/event.h>
#include <sys/mman.h>

#include <aio.h>
#include <atf-c.h>
#include <mqueue.h>
#include <pthread.h>
#include <signal.h>

#include <compat/sys/event.h>
#include <compat/sys/mman.h>
#include <compat/sys/poll.h>
#include <compat/sys/select.h>

#include <compat/include/aio.h>
#include <compat/include/mqueue.h>
#include <compat/include/signal.h>
#include <compat/include/time.h>

#include "cancelpoint.h"
#include "h_macros.h"

pthread_barrier_t bar;
bool cleanup_done;

static void
cancelpoint_compat100_kevent(void)
{
        int kq;
        struct kevent100 ev;

        memset(&ev, 0, sizeof(ev));
        ev.ident = SIGUSR1;
        ev.filter = EVFILT_SIGNAL;
        ev.flags = EV_ADD|EV_ENABLE;
        ev.fflags = 0;
        ev.data = 0;
        ev.udata = 0;

        RL(kq = kqueue());
        RL(__kevent50(kq, &ev, 1, NULL, 1, &(const struct timespec){0,0}));
        cancelpointready();
        RL(__kevent50(kq, NULL, 0, &ev, 1, NULL));
}

static void
cancelpoint_compat12_msync(void)
{
        const unsigned long pagesize = sysconf(_SC_PAGESIZE);
        int fd;
        void *map;

        RL(fd = open("file", O_RDWR|O_CREAT, 0666));
        RL(ftruncate(fd, pagesize));
        REQUIRE_LIBC(map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
                MAP_SHARED, fd, 0),
            MAP_FAILED);
        cancelpointready();
        RL(msync(map, pagesize));
}

static void
cancelpoint_compat50___sigtimedwait(void)
{
        sigset_t mask, omask;
        siginfo_t info;
        struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};

        RL(__sigfillset14(&mask));
        RL(__sigprocmask14(SIG_BLOCK, &mask, &omask));
        cancelpointready();
        RL(__sigtimedwait(&omask, &info, &t));
}

static void
cancelpoint_compat50_aio_suspend(void)
{
        int fd[2];
        char buf[32];
        struct aiocb aio = {
                .aio_offset = 0,
                .aio_buf = buf,
                .aio_nbytes = sizeof(buf),
                .aio_fildes = -1,
        };
        const struct aiocb *const aiolist[] = { &aio };

        RL(pipe(fd));
        aio.aio_fildes = fd[0];
        RL(aio_read(&aio));
        cancelpointready();
        RL(aio_suspend(aiolist, __arraycount(aiolist), NULL));
}

static void
cancelpoint_compat50_kevent(void)
{
        int kq;
        struct kevent100 ev;

        memset(&ev, 0, sizeof(ev));
        ev.ident = SIGUSR1;
        ev.filter = EVFILT_SIGNAL;
        ev.flags = EV_ADD|EV_ENABLE;
        ev.fflags = 0;
        ev.data = 0;
        ev.udata = 0;

        RL(kq = kqueue());
        RL(kevent(kq, &ev, 1, NULL, 1, &(const struct timespec50){0,0}));
        cancelpointready();
        RL(kevent(kq, NULL, 0, &ev, 1, NULL));
}

static void
cancelpoint_compat50_mq_timedreceive(void)
{
        mqd_t mq;
        char buf[32];
        struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};

        RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
        cancelpointready();
        RL(mq_timedreceive(mq, buf, sizeof(buf), NULL, &t));
}

static void
cancelpoint_compat50_mq_timedsend(void)
{
        mqd_t mq;
        char buf[32] = {0};
        struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};

        RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
        cancelpointready();
        RL(mq_timedsend(mq, buf, sizeof(buf), 0, &t));
}

static void
cancelpoint_compat50_nanosleep(void)
{
        struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};

        cancelpointready();
        RL(nanosleep(&t, NULL));
}

static void
cancelpoint_compat50_pollts(void)
{
        int fd[2];
        struct pollfd pfd;
        struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};

        RL(pipe(fd));
        pfd.fd = fd[0];
        pfd.events = POLLIN;
        cancelpointready();
        RL(pollts(&pfd, 1, &t, NULL));
}

static void
cancelpoint_compat50_pselect(void)
{
        int fd[2];
        fd_set readfd;
        struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};

        FD_ZERO(&readfd);

        RL(pipe(fd));
        FD_SET(fd[0], &readfd);
        cancelpointready();
        RL(pselect(fd[0] + 1, &readfd, NULL, NULL, &t, NULL));
}

static void
cancelpoint_compat50_select(void)
{
        int fd[2];
        fd_set readfd;
        struct timeval50 t = {.tv_sec = 1, .tv_usec = 0};

        FD_ZERO(&readfd);

        RL(pipe(fd));
        FD_SET(fd[0], &readfd);
        cancelpointready();
        RL(select(fd[0] + 1, &readfd, NULL, NULL, &t));
}

static void
cancelpoint_compat13_sigsuspend(void)
{
        sigset13_t mask, omask;

        RL(sigfillset(&mask));
        RL(sigprocmask(SIG_BLOCK, &mask, &omask));
        cancelpointready();
        RL(sigsuspend(&omask));
}

static void
cancelpoint_compat50_sigtimedwait(void)
{
        sigset_t mask, omask;
        siginfo_t info;
        struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};

        RL(__sigfillset14(&mask));
        RL(__sigprocmask14(SIG_BLOCK, &mask, &omask));
        cancelpointready();
        RL(sigtimedwait(&omask, &info, &t));
}

TEST_CANCELPOINT(cancelpoint_compat100_kevent, __nothing)
TEST_CANCELPOINT(cancelpoint_compat12_msync, __nothing)
TEST_CANCELPOINT(cancelpoint_compat13_sigsuspend,
    atf_tc_expect_signal(-1, "PR lib/59240: POSIX.1-2024:"
        " cancellation point audit"))
TEST_CANCELPOINT(cancelpoint_compat50___sigtimedwait,
    atf_tc_expect_signal(-1, "PR lib/59240: POSIX.1-2024:"
        " cancellation point audit"))
TEST_CANCELPOINT(cancelpoint_compat50_aio_suspend, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_kevent, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_mq_timedreceive, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_mq_timedsend, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_nanosleep, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_pollts, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_pselect, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_select, __nothing)
TEST_CANCELPOINT(cancelpoint_compat50_sigtimedwait,
    atf_tc_expect_signal(-1, "PR lib/59240: POSIX.1-2024:"
        " cancellation point audit"))

ATF_TP_ADD_TCS(tp)
{

        ADD_TEST_CANCELPOINT(cancelpoint_compat100_kevent);
        ADD_TEST_CANCELPOINT(cancelpoint_compat12_msync);
        ADD_TEST_CANCELPOINT(cancelpoint_compat13_sigsuspend);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50___sigtimedwait);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_aio_suspend);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_kevent);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_mq_timedreceive);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_mq_timedsend);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_nanosleep);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_pollts);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_pselect);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_select);
        ADD_TEST_CANCELPOINT(cancelpoint_compat50_sigtimedwait);

        return atf_no_error();
}