Skip to content

Commit

Permalink
futex(2): Fix FUTEX_CMP_REQUEUE to always compare even if no waiters.
Browse files Browse the repository at this point in the history
It must always compare the futex value and fail with EAGAIN on
mismatch, even if there are no waiters.

  FUTEX_CMP_REQUEUE (since Linux 2.6.7)
         This operation first checks whether the location uaddr
         still contains the value val3.  If not, the operation
         fails with the error EAGAIN.  Otherwise, the operation [...]

https://man7.org/linux/man-pages/man2/futex.2.html

PR kern/56828: futex calls in Linux emulation sometimes hang
  • Loading branch information
riastradh committed Jan 18, 2025
1 parent bb8b192 commit 42de50b
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 11 deletions.
21 changes: 15 additions & 6 deletions sys/kern/sys_futex.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $NetBSD: sys_futex.c,v 1.20 2024/04/11 13:51:36 riastradh Exp $ */
/* $NetBSD: sys_futex.c,v 1.21 2025/01/18 07:26:06 riastradh Exp $ */

/*-
* Copyright (c) 2018, 2019, 2020 The NetBSD Foundation, Inc.
Expand Down Expand Up @@ -30,7 +30,7 @@
*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.20 2024/04/11 13:51:36 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.21 2025/01/18 07:26:06 riastradh Exp $");

/*
* Futexes
Expand Down Expand Up @@ -1336,14 +1336,23 @@ futex_func_requeue(bool shared, int op, int *uaddr, int val, int *uaddr2,
goto out;
}

/* Look up the source futex, if any. */
error = futex_lookup(uaddr, shared, &f);
/*
* Look up or create the source futex. For FUTEX_CMP_REQUEUE,
* we always create it, rather than bail if it has no waiters,
* because FUTEX_CMP_REQUEUE always tests the futex word in
* order to report EAGAIN.
*/
error = (op == FUTEX_CMP_REQUEUE
? futex_lookup_create(uaddr, shared, &f)
: futex_lookup(uaddr, shared, &f));
if (error)
goto out;

/* If there is none, nothing to do. */
if (f == NULL)
/* If there is none for FUTEX_REQUEUE, nothing to do. */
if (f == NULL) {
KASSERT(op != FUTEX_CMP_REQUEUE);
goto out;
}

/*
* We may need to create the destination futex because it's
Expand Down
7 changes: 2 additions & 5 deletions tests/lib/libc/sys/t_futex_ops.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $NetBSD: t_futex_ops.c,v 1.8 2025/01/18 07:05:15 riastradh Exp $ */
/* $NetBSD: t_futex_ops.c,v 1.9 2025/01/18 07:26:06 riastradh Exp $ */

/*-
* Copyright (c) 2019, 2020 The NetBSD Foundation, Inc.
Expand Down Expand Up @@ -29,7 +29,7 @@
#include <sys/cdefs.h>
__COPYRIGHT("@(#) Copyright (c) 2019, 2020\
The NetBSD Foundation, inc. All rights reserved.");
__RCSID("$NetBSD: t_futex_ops.c,v 1.8 2025/01/18 07:05:15 riastradh Exp $");
__RCSID("$NetBSD: t_futex_ops.c,v 1.9 2025/01/18 07:26:06 riastradh Exp $");

#include <sys/fcntl.h>
#include <sys/mman.h>
Expand Down Expand Up @@ -927,13 +927,10 @@ ATF_TC_BODY(futex_cmp_requeue_trivial, tc)

futex_word = 123;
futex_word1 = 456; /* should be ignored */
atf_tc_expect_fail("PR kern/56828:"
" futex calls in Linux emulation sometimes hang");
ATF_CHECK_ERRNO(EAGAIN, __futex(&futex_word, FUTEX_CMP_REQUEUE,
/*nwake*/1, NULL, &futex_word1, /*nrequeue*/1, 0) == -1);
ATF_CHECK_ERRNO(EAGAIN, __futex(&futex_word, FUTEX_CMP_REQUEUE,
/*nwake*/1, NULL, &futex_word1, /*nrequeue*/1, 122) == -1);
atf_tc_expect_pass();
nwoken = __futex(&futex_word, FUTEX_CMP_REQUEUE,
/*nwake*/1, NULL, &futex_word1, /*nrequeue*/1, 123);
ATF_CHECK_MSG(nwoken != -1, "errno=%d (%s)", errno, strerror(errno));
Expand Down

0 comments on commit 42de50b

Please sign in to comment.