From nobody Mon Jun 8 08:53:39 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B3A63477E42; Wed, 3 Jun 2026 14:24:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780496693; cv=none; b=NQcFnS6zlpziVsaKenlLdASG5wQjnXkfoRMwA90V5iazFvZgnxqV1qeqctxixbHmza0+Nm4ulz725/UulVR1ip68+FVHHLu5sI/ojeA/HUJ1JPetnwe9Ktt/SC5jMmCfgTEkTSheB1FemPW0tQGkDiHssVJ0bi/RJRAq1PuSitI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780496693; c=relaxed/simple; bh=tNz+2t77Ki8pcGNEcvWtOJEYq5/HZwdE+WvFei1G0sI=; h=Date:From:To:Subject:Cc:In-Reply-To:References:MIME-Version: Message-ID:Content-Type; b=qyKosPMmGFKXXVfrji1Hr5FT+uPqfEBBMrVUoAqKEKQwxD7N9skqqDXgAf5YDVcKIvLK0OXBdIx4IqUdFmA3V3XGdE523u/sDZYANN6eHBZq3y/AU5GHbqGs1kiLG1+keTTPthtCllCbcQal5/f3lC1tVUCDDgE8o5hqpNDpe2s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=fJjE+OmX; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=06DhFCop; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="fJjE+OmX"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="06DhFCop" Date: Wed, 03 Jun 2026 14:24:48 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1780496689; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=C3uQtJqweesYKkiWVVH5rKTpUN/Sp+EbpuTl8Hl5AU4=; b=fJjE+OmXJjCGeIzuTq5az3Xbd7SIbHjx6TZXmJFQ6u8FOF+Bt+Yq8e49UT9Yiwgr3I5Zm/ f6efZSSJWDCyPOjoRmlcTLeG8xHToOtxPs4mUAMpg+CtLSJtO+63HcMUnC/zbhmPzV4iTZ NC03GGXJdRWa1yOMtllM5QC7bk6A4e4TufwMrtpDs3FIhdCfaJv5xPoDYE/R+DUiiG+xLu jZac10trbMyadFu+r0mkRIQql+EcyeBFW9aQxgPlOLzEc/ro1P1zhAKdepGN8MuEUCtRnz PvttGyTZFyJzmsrzDyWEu//+Y4Hzp5AaqGfn7SjxliTmQlmYEiBBO+gO1oe2zg== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1780496689; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=C3uQtJqweesYKkiWVVH5rKTpUN/Sp+EbpuTl8Hl5AU4=; b=06DhFCopws5vXui4KY26M7uOcrh18ka9MXECY6npZntKQICVVGUE40TZyWx9iPNIyAt1Gr uvuRIMHeivN5sNCg== From: tip-bot2 for =?utf-8?q?Andr=C3=A9?= Almeida Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: locking/core] selftests: futex: Add tests for robust release operations Cc: andrealmeid@igalia.com, Thomas Gleixner , "Peter Zijlstra (Intel)" , x86@kernel.org, linux-kernel@vger.kernel.org In-Reply-To: <20260602090535.988101541@kernel.org> References: <20260602090535.988101541@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <178049668830.710.11975688054063183686.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the locking/core branch of tip: Commit-ID: 608323bf7bb85bbb647eca4373acef247f105e67 Gitweb: https://git.kernel.org/tip/608323bf7bb85bbb647eca4373acef247= f105e67 Author: Andr=C3=A9 Almeida AuthorDate: Tue, 02 Jun 2026 11:10:21 +02:00 Committer: Peter Zijlstra CommitterDate: Wed, 03 Jun 2026 11:38:53 +02:00 selftests: futex: Add tests for robust release operations Add tests for __vdso_futex_robust_listXX_try_unlock() and for the futex() op FUTEX_ROBUST_UNLOCK. Test the contended and uncontended cases for the vDSO functions and all ops combinations for FUTEX_ROBUST_UNLOCK. [ tglx: Replace the VDSO function lookup ] Signed-off-by: Andr=C3=A9 Almeida Signed-off-by: Thomas Gleixner Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260329-tonyk-vdso_test-v2-2-b7db810e44a1@i= galia.com Link: https://patch.msgid.link/20260602090535.988101541@kernel.org --- tools/testing/selftests/futex/functional/robust_list.c | 239 ++++++++- tools/testing/selftests/futex/include/futextest.h | 6 +- 2 files changed, 245 insertions(+) diff --git a/tools/testing/selftests/futex/functional/robust_list.c b/tools= /testing/selftests/futex/functional/robust_list.c index e7d1254..b3fab60 100644 --- a/tools/testing/selftests/futex/functional/robust_list.c +++ b/tools/testing/selftests/futex/functional/robust_list.c @@ -27,12 +27,15 @@ #include "futextest.h" #include "../../kselftest_harness.h" =20 +#include #include #include #include +#include #include #include #include +#include #include #include =20 @@ -42,6 +45,10 @@ =20 #define SLEEP_US 100 =20 +#if __SIZEOF_LONG__ =3D=3D 8 +# define BUILD_64 +#endif + static pthread_barrier_t barrier, barrier2; =20 static int set_robust_list(struct robust_list_head *head, size_t len) @@ -54,6 +61,12 @@ static int get_robust_list(int pid, struct robust_list_h= ead **head, size_t *len_ return syscall(SYS_get_robust_list, pid, head, len_ptr); } =20 +static int sys_futex_robust_unlock(_Atomic(uint32_t) *uaddr, unsigned int = op, int val, + void *list_op_pending, unsigned int val3) +{ + return syscall(SYS_futex, uaddr, op, val, NULL, list_op_pending, val3, 0); +} + /* * Basic lock struct, contains just the futex word and the robust list ele= ment * Real implementations have also a *prev to easily walk in the list @@ -549,4 +562,230 @@ TEST(test_circular_list) ksft_test_result_pass("%s\n", __func__); } =20 +/* + * Below are tests for the fix of robust release race condition. Please re= ad the following + * thread to learn more about the issue in the first place and why the fol= lowing functions fix it: + * https://lore.kernel.org/lkml/20260316162316.356674433@kernel.org/ + */ + +/* + * Auxiliary code for binding the vDSO functions + */ +static void *get_vdso_func_addr(const char *function) +{ + const char *vdso_names[] =3D { + "linux-vdso.so.1", "linux-gate.so.1", "linux-vdso32.so.1", "linux-vdso64= .so.1", + }; + + for (int i =3D 0; i < ARRAY_SIZE(vdso_names); i++) { + void *vdso =3D dlopen(vdso_names[i], RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOA= D); + + if (vdso) + return dlsym(vdso, function); + } + return NULL; +} + +/* + * These are the real vDSO function signatures: + * + * __vdso_futex_robust_list64_try_unlock(__u32 *lock, __u32 tid, __u64 *po= p) + * __vdso_futex_robust_list32_try_unlock(__u32 *lock, __u32 tid, __u32 *po= p) + * + * So for the generic entry point we need to use a void pointer as the las= t argument + */ +FIXTURE(vdso_unlock) +{ + uint32_t (*vdso)(_Atomic(uint32_t) *lock, uint32_t tid, void *pop); +}; + +FIXTURE_VARIANT(vdso_unlock) +{ + bool is_32; + char func_name[]; +}; + +FIXTURE_SETUP(vdso_unlock) +{ + self->vdso =3D get_vdso_func_addr(variant->func_name); +} + +FIXTURE_TEARDOWN(vdso_unlock) {} + +FIXTURE_VARIANT_ADD(vdso_unlock, 32) +{ + .func_name =3D "__vdso_futex_robust_list32_try_unlock", + .is_32 =3D true, +}; + +FIXTURE_VARIANT_ADD(vdso_unlock, 64) +{ + .func_name =3D "__vdso_futex_robust_list64_try_unlock", + .is_32 =3D false, +}; + +/* + * Test the vDSO robust_listXX_try_unlock() for the uncontended case. The = virtual syscall should + * return the thread ID of the lock owner, the lock word must be 0 and the= list_op_pending should + * be NULL. + */ +TEST_F(vdso_unlock, test_robust_try_unlock_uncontended) +{ + struct lock_struct lock =3D { .futex =3D 0 }; + _Atomic(unsigned int) *futex =3D &lock.futex; + struct robust_list_head head; + uintptr_t exp =3D (uintptr_t) NULL; + pid_t tid =3D gettid(); + int ret; + + if (!self->vdso) { + ksft_test_result_skip("%s not found\n", variant->func_name); + return; + } + + *futex =3D tid; + + ret =3D set_list(&head); + if (ret) + ksft_test_result_fail("set_robust_list error\n"); + + head.list_op_pending =3D &lock.list; + + ret =3D self->vdso(futex, tid, &head.list_op_pending); + + ASSERT_EQ(ret, tid); + ASSERT_EQ(*futex, 0); + + /* Check only the lower 32 bits for the 32-bit entry point */ + if (variant->is_32) { + exp =3D (uintptr_t)(unsigned long)&lock.list; + exp &=3D ~0xFFFFFFFFULL; + } + + ASSERT_EQ((uintptr_t)(unsigned long)head.list_op_pending, exp); +} + +/* + * If the lock is contended, the operation fails. The return value is the = value found at the + * futex word (tid | FUTEX_WAITERS), the futex word is not modified and th= e list_op_pending is_32 + * not cleared. + */ +TEST_F(vdso_unlock, test_robust_try_unlock_contended) +{ + struct lock_struct lock =3D { .futex =3D 0 }; + _Atomic(unsigned int) *futex =3D &lock.futex; + struct robust_list_head head; + pid_t tid =3D gettid(); + int ret; + + if (!self->vdso) { + ksft_test_result_skip("%s not found\n", variant->func_name); + return; + } + + *futex =3D tid | FUTEX_WAITERS; + + ret =3D set_list(&head); + if (ret) + ksft_test_result_fail("set_robust_list error\n"); + + head.list_op_pending =3D &lock.list; + + ret =3D self->vdso(futex, tid, &head.list_op_pending); + + ASSERT_EQ(ret, tid | FUTEX_WAITERS); + ASSERT_EQ(*futex, tid | FUTEX_WAITERS); + ASSERT_EQ(head.list_op_pending, &lock.list); +} + +FIXTURE(futex_op) {}; + +FIXTURE_VARIANT(futex_op) +{ + unsigned int op; + unsigned int val3; +}; + +FIXTURE_SETUP(futex_op) {} + +FIXTURE_TEARDOWN(futex_op) {} + +FIXTURE_VARIANT_ADD(futex_op, wake) +{ + .op =3D FUTEX_WAKE, + .val3 =3D 0, +}; + +FIXTURE_VARIANT_ADD(futex_op, wake_bitset) +{ + .op =3D FUTEX_WAKE_BITSET, + .val3 =3D FUTEX_BITSET_MATCH_ANY, +}; + +FIXTURE_VARIANT_ADD(futex_op, unlock_pi) +{ + .op =3D FUTEX_UNLOCK_PI, + .val3 =3D 0, +}; + +FIXTURE_VARIANT_ADD(futex_op, wake32) +{ + .op =3D FUTEX_WAKE | FUTEX_ROBUST_LIST32, + .val3 =3D 0, +}; + +FIXTURE_VARIANT_ADD(futex_op, wake_bitset32) +{ + .op =3D FUTEX_WAKE_BITSET | FUTEX_ROBUST_LIST32, + .val3 =3D FUTEX_BITSET_MATCH_ANY, +}; + +FIXTURE_VARIANT_ADD(futex_op, unlock_pi32) +{ + .op =3D FUTEX_UNLOCK_PI | FUTEX_ROBUST_LIST32, + .val3 =3D 0, +}; + +/* + * The syscall should return the number of tasks waken (for this test, 0),= clear the futex word and + * clear list_op_pending + */ +TEST_F(futex_op, test_futex_robust_unlock) +{ + struct lock_struct lock =3D { .futex =3D 0 }; + _Atomic(unsigned int) *futex =3D &lock.futex; + uintptr_t exp =3D (uintptr_t) NULL; + struct robust_list_head head; + pid_t tid =3D gettid(); + int ret; + +#ifndef BUILD_64 + if (!(variant->op & FUTEX_ROBUST_LIST32)) { + ksft_test_result_skip("Not supported for 32 bit build\n"); + return; + } +#endif + + *futex =3D tid | FUTEX_WAITERS; + + ret =3D set_list(&head); + if (ret) + ksft_test_result_fail("set_robust_list error\n"); + + head.list_op_pending =3D &lock.list; + + ret =3D sys_futex_robust_unlock(futex, FUTEX_ROBUST_UNLOCK | variant->op,= tid, + &head.list_op_pending, variant->val3); + + ASSERT_EQ(ret, 0); + ASSERT_EQ(*futex, 0); + + if (variant->op & FUTEX_ROBUST_LIST32) { + exp =3D (uint64_t)(unsigned long)&lock.list; + exp &=3D ~0xFFFFFFFFULL; + } + + ASSERT_EQ((uintptr_t)(unsigned long)head.list_op_pending, exp); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/futex/include/futextest.h b/tools/test= ing/selftests/futex/include/futextest.h index 3d48e97..df33f31 100644 --- a/tools/testing/selftests/futex/include/futextest.h +++ b/tools/testing/selftests/futex/include/futextest.h @@ -38,6 +38,12 @@ typedef volatile u_int32_t futex_t; #ifndef FUTEX_CMP_REQUEUE_PI #define FUTEX_CMP_REQUEUE_PI 12 #endif +#ifndef FUTEX_ROBUST_UNLOCK +#define FUTEX_ROBUST_UNLOCK 512 +#endif +#ifndef FUTEX_ROBUST_LIST32 +#define FUTEX_ROBUST_LIST32 1024 +#endif #ifndef FUTEX_WAIT_REQUEUE_PI_PRIVATE #define FUTEX_WAIT_REQUEUE_PI_PRIVATE (FUTEX_WAIT_REQUEUE_PI | \ FUTEX_PRIVATE_FLAG)