NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/60846: fork() disrupts futex



>Number:         60846
>Category:       kern
>Synopsis:       fork() disrupts futex
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Oct 03 18:00:01 +0000 2026
>Originator:     Thomas Klausner
>Release:        NetBSD 11.99.9/20261002
>Organization:
	
>Environment:
	
	
Architecture: x86_64
Machine: amd64
>Description:
Still trying to get a Linux program running, I found a weird bug in the futex handling.

A thread gets started and waits for a private futex;
the main thread wakes the private futex - this works.

However, when in between the thread start and the futex wake,
a child is forked (and reaped), the futex wake is not received by the
waiting thread.
>How-To-Repeat:

Save the test program below as futex-test.c and compile it on Linux,
or with pkgsrc/emulators/suse_gcc12 with:

/emul/linux/usr/bin/gcc-12 -o futex-test futex-test.c -lpthread

By default, it does not fork, but if the first command line argument is 'fork' it does.

Running it both ways on Debian 12.15 gives:

--- Linux output ---
# ./futex-test ; echo; ./futex-test fork
cc -o futex-test futex-test.c -lpthread
thread: futex waiting on address 0x557c663b0078
main: waking futex on address 0x557c663b0078
main: futex wake returned 1; joining
thread: futex wait return value 0 errno 0
RESULT: woken OK

thread: futex waiting on address 0x55ec13905078
main: forking child
child: sleeping
child: exiting
main: child reaped, return value 1971108, status 0
main: waking futex on address 0x55ec13905078
main: futex wake returned 1; joining
thread: futex wait return value 0 errno 0
RESULT: woken OK
--- end Linux output ---

while running it both ways on NetBSD-current's Linux emulation gives

--- NetBSD output ---
./futex-test; echo; ./futex-test fork
/emul/linux/usr/bin/gcc-12 -o futex-test futex-test.c -lpthread
thread: futex waiting on address 0x602090
main: waking futex on address 0x602090
main: futex wake returned 1; joining
thread: futex wait return value 0 errno 0
RESULT: woken OK

thread: futex waiting on address 0x602090
main: forking child
child: sleeping
child: exiting
main: child reaped, return value 8019, status 0
main: waking futex on address 0x602090
main: futex wake returned 0; joining
RESULT: WAKE LOST (join timed out)
--- end NetBSD output ---

--- begin futex-test.c ---
#define _GNU_SOURCE
#include <errno.h>
#include <linux/futex.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

static volatile int __attribute__((aligned(4))) word = 1;

static void *waiter(void *arg)
{
	(void)arg;
	errno = 0;
	fprintf(stdout, "thread: futex waiting on address %p\n", &word);
	long r = syscall(SYS_futex, &word, FUTEX_WAIT_PRIVATE, 1, NULL, NULL, 0);
	printf("thread: futex wait return value %ld errno %d\n", r, errno);
	return NULL;
}

int main(int argc, char **argv)
{
	pthread_t tw;
	struct timespec dl;
	int do_fork = (argc > 1 && strcmp(argv[1], "fork") == 0);

	pthread_create(&tw, NULL, waiter, NULL);
	/* give thread time to block */
	usleep(500 * 1000);

	if (do_fork) {
		printf("main: forking child\n");
		pid_t c = fork();
		if (c == 0) {
			printf("child: sleeping\n");
			usleep(1000 * 1000);
			printf("child: exiting\n");
			_exit(0);
		}
		usleep(1000 * 1000);
		int status, ret;
		ret = waitpid(c, &status, 0);
		printf("main: child reaped, return value %d, status %d\n", ret, status);
	}

	word = 2;
	fprintf(stdout, "main: waking futex on address %p\n", &word);
	long nw = syscall(SYS_futex, &word, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
	printf("main: futex wake returned %ld; joining\n", nw);

	clock_gettime(CLOCK_REALTIME, &dl);
	dl.tv_sec += 3;
	if (pthread_timedjoin_np(tw, NULL, &dl) == 0) {
		printf("RESULT: woken OK\n");
		return 0;
	}
	printf("RESULT: WAKE LOST (join timed out)\n");
	return 1;
}
--- end ---

>Fix:
Yes, please!

>Unformatted:
 	
 	



Home | Main Index | Thread Index | Old Index