NetBSD Problem Report #60846
From wiz@netbsd.org Sat Oct 3 17:58:21 2026
Return-Path: <wiz@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
client-signature RSA-PSS (2048 bits) client-digest SHA256)
(Client CN "mail.netbsd.org", Issuer "YR1" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id 0A1EB1A923C
for <gnats-bugs@gnats.NetBSD.org>; Sat, 3 Oct 2026 17:58:20 +0000 (UTC)
Message-Id: <20261003175820.0A0AC84D93@mail.netbsd.org>
Date: Sat, 3 Oct 2026 17:58:20 +0000 (UTC)
From: wiz@NetBSD.org
Reply-To: wiz@NetBSD.org
To: gnats-bugs@NetBSD.org
Subject: fork() disrupts futex
X-Send-Pr-Version: 3.95
X-From4GNATS: "wiz@NetBSD.org via gnats" <gnats-admin@NetBSD.org>
>Number: 60846
>Category: kern
>Synopsis: fork() disrupts futex
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: chs
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Oct 03 18:00:01 +0000 2026
>Last-Modified: Sun Oct 04 21:59:44 +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!
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: kern-bug-people->chs
Responsible-Changed-By: chs@NetBSD.org
Responsible-Changed-When: Sun, 04 Oct 2026 21:59:44 +0000
Responsible-Changed-Why:
I have patches that fix this.
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.