---------- Forwarded message ---------
From: Thomas Munro <thomas.mu...@gmail.com>
Date: Tue, Oct 26, 2021 at 5:36 AM
Subject: futex(2) not working in inherited mmap'd anon memory
To: <m...@openbsd.org>


Hello,

When I do mmap(MAP_ANONYMOUS | MAP_SHARED) and then fork(), it seems
that futex(2) wakeups are not delivered between child and parent in
that memory.  It does work as expected if I instead use
shmget(IPC_PRIVATE).

Below is a standalone test program.  I tested it with the four OSes
mentioned, and the two shmem types depending on that #if, and all
worked as expected except the OpenBSD/mmap case, which hangs.

Is it a bug?

$ uname -a
OpenBSD openbsd6.localdomain 6.9 GENERIC.MP#473 amd64

Thanks,

=== 8< ===

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#if defined(__linux__)
#include <linux/futex.h>
#include <sys/syscall.h>
#elif defined(__OpenBSD__)
#include <sys/time.h>
#include <sys/futex.h>
#elif defined(__FreeBSD__)
#include <sys/types.h>
#include <sys/umtx.h>
#elif defined(__APPLE__)
#define UL_COMPARE_AND_WAIT_SHARED 3
#define ULF_WAKE_ALL 0x00000100
extern int __ulock_wait(uint32_t operation, void *addr, uint64_t
value, uint32_t timeout);
extern int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
#endif

static int
my_futex_wait_u32(void *fut, uint32_t value, struct timespec *timeout)
{
#if defined(__linux__)
        if (syscall(SYS_futex, fut, FUTEX_WAIT, value, timeout, 0, 0) == 0)
                return 0;
#elif defined(__OpenBSD__)
        if ((errno = futex(fut, FUTEX_WAIT, (int) value, timeout, NULL)) == 0)
                return 0;
        if (errno == ECANCELED)
                errno = EINTR;
#elif defined(__FreeBSD__)
        if (_umtx_op(fut, UMTX_OP_WAIT_UINT, value, 0, timeout) == 0)
                return 0;
#elif defined (__APPLE__)
        if (__ulock_wait(UL_COMPARE_AND_WAIT_SHARED, (void *) fut,
value, timeout ? timeout->tv_sec * 1000000 + timeout->tv_nsec / 1000 :
0) >
= 0)
                return 0;
#else
        errno = ENOSYS;
#endif

        return -1;
}

static int
my_futex_wake(void *fut, int nwaiters)
{
#if defined(__linux__)
        if (syscall(SYS_futex, fut, FUTEX_WAKE, nwaiters, NULL, 0, 0) >= 0)
                return 0;
#elif defined(__OpenBSD__)
        if (futex(fut, FUTEX_WAKE, nwaiters, NULL, NULL) >= 0)
                return 0;
#elif defined(__FreeBSD__)
        if (_umtx_op(fut, UMTX_OP_WAKE, nwaiters, 0, 0) == 0)
                return 0;
#elif defined (__APPLE__)
        if (__ulock_wake(UL_COMPARE_AND_WAIT_SHARED | (nwaiters > 1 ?
ULF_WAKE_ALL : 0), (void *) fut, 0) >= 0)
                return 0;
        if (errno == ENOENT)
                return 0;
#else
        errno = ENOSYS;
#endif

        return -1;
}

int
main(int argc, char *argv[])
{
        pid_t pid;
        uint32_t *memory;
        int status;

#if 1
        memory = mmap(NULL, sizeof(uint32_t), PROT_READ | PROT_WRITE,
                MAP_ANONYMOUS | MAP_SHARED, -1, 0);
        if (memory == MAP_FAILED) {
                perror("mmap");
                return EXIT_FAILURE;
        }
#else
        int shm_id;

        shm_id = shmget(IPC_PRIVATE, sizeof(uint32_t), IPC_CREAT | 0666);
        if (shm_id < 0) {
                perror("shmget");
                return EXIT_FAILURE;
        }
        memory = shmat(shm_id, NULL, 0);
        if ((intptr_t) memory == -1) {
                perror("shmat");
                return EXIT_FAILURE;
        }
#endif

        *memory = 42;

        pid = fork();
        if (pid == -1) {
                perror("fork");
                return EXIT_FAILURE;
        } else if (pid > 0) {
                printf("hello from parent, will wait for futex...\n");
                if (my_futex_wait_u32(memory, 42, NULL) < 0) {
                        perror("futex_wait");
                        wait(&status);
                        return EXIT_FAILURE;
                }
                wait(&status);
                return EXIT_SUCCESS;
        } else {
                printf("hello from child, will wake futex...\n");
                sleep(1);
                if (my_futex_wake(memory, INT_MAX) < 0) {
                        perror("futex_wake");
                        return EXIT_FAILURE;
                }
                return EXIT_SUCCESS;
        }
}

Reply via email to