[PATCH v7 10/10] futex: Use new robust list API internally
From: André Almeida
Date: Fri Sep 25 2026 - 13:56:47 EST
The new robust list API internals can handle any kind of robust list, so
to simplify the code, reuse the same mechanisms for the original API and
when calling the original set syscall, set the head in the array of
lists. The first two indexes of the array of robust lists are reserved
for the original API lists, the native robust list and the compat robust
list.
Signed-off-by: André Almeida <andrealmeid@xxxxxxxxxx>
---
include/linux/futex.h | 4 ----
include/linux/futex_types.h | 6 ------
kernel/futex/core.c | 34 +++++++++++++++---------------
kernel/futex/syscalls.c | 50 +++++++++++++++++++++++----------------------
4 files changed, 44 insertions(+), 50 deletions(-)
diff --git a/include/linux/futex.h b/include/linux/futex.h
index 049dbe7aed18..f8987e2a0fc3 100644
--- a/include/linux/futex.h
+++ b/include/linux/futex.h
@@ -91,10 +91,6 @@ static inline void futex_init_task(struct task_struct *tsk)
INIT_LIST_HEAD(&tsk->futex.pi_state_list);
tsk->futex.state = FUTEX_STATE_OK;
mutex_init(&tsk->futex.exit_mutex);
- tsk->futex.robust_list = NULL;
-#ifdef CONFIG_COMPAT
- tsk->futex.robust_list32 = NULL;
-#endif
tsk->futex.robust_lists = NULL;
}
diff --git a/include/linux/futex_types.h b/include/linux/futex_types.h
index 089a9f3edfe1..9b03629d0bd1 100644
--- a/include/linux/futex_types.h
+++ b/include/linux/futex_types.h
@@ -13,8 +13,6 @@ struct robust_list_head;
/**
* struct futex_sched_data - Futex related per task data
- * @robust_list: User space registered robust list pointer
- * @compat_robust_list: User space registered robust list pointer for compat tasks
* @pi_state_list: List head for Priority Inheritance (PI) state management
* @pi_state_cache: Pointer to cache one PI state object per task
* @exit_mutex: Mutex for serializing exit
@@ -22,10 +20,6 @@ struct robust_list_head;
* @robust_lists: List of robust lists heads
*/
struct futex_sched_data {
- struct robust_list_head __user *robust_list;
-#ifdef CONFIG_COMPAT
- struct robust_list_head32 __user *robust_list32;
-#endif
struct list_head pi_state_list;
struct futex_pi_state *pi_state_cache;
struct mutex exit_mutex;
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index 4a692f169cef..40753112ad6b 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -96,6 +96,13 @@ int futex_robust_list_create(uintptr_t head, enum robust_list2_cmd cmd)
}
switch (cmd) {
+ case FUTEX_ROBUST_LIST_SET_COMPAT:
+ entry |= FUTEX_ROBUST_LIST_ENTRY_32BIT;
+ index = FUTEX_ROBUST_LIST_COMPAT_IDX;
+ fallthrough;
+ case FUTEX_ROBUST_LIST_SET_NATIVE:
+ index = FUTEX_ROBUST_LIST_NATIVE_IDX;
+ goto exit;
case FUTEX_ROBUST_LIST_CMD_CREATE_32:
entry |= FUTEX_ROBUST_LIST_ENTRY_32BIT;
fallthrough;
@@ -1284,7 +1291,8 @@ static void exit_robust_list(struct task_struct *curr, struct robust_list_head _
static bool robust_list_clear_pending(unsigned long __user *pop)
{
- struct robust_list_head __user *head = current->futex.robust_list;
+ struct robust_list_head __user *head = (struct robust_list_head __user *)
+ current->futex.robust_lists[FUTEX_ROBUST_LIST_NATIVE_IDX];
if (!put_user(0UL, pop))
return true;
@@ -1299,7 +1307,8 @@ static bool robust_list_clear_pending(unsigned long __user *pop)
* that's mostly an academic exercise.
*/
if (pop == (unsigned long __user *)&head->list_op_pending)
- current->futex.robust_list = NULL;
+ current->futex.robust_lists[FUTEX_ROBUST_LIST_NATIVE_IDX] = 0;
+
return false;
}
@@ -1402,14 +1411,16 @@ static void exit_robust_list32(struct task_struct *curr, struct robust_list_head
static bool robust_list_clear_pending32(u32 __user *pop)
{
- struct robust_list_head32 __user *head = current->futex.robust_list32;
+ struct robust_list_head32 __user *head =
+ (struct robust_list_head32 __user *)
+ current->futex.robust_lists[FUTEX_ROBUST_LIST_COMPAT_IDX];
if (!put_user(0U, pop))
return true;
/* See comment in robust_list_clear_pending(). */
if (pop == &head->list_op_pending)
- current->futex.robust_list32 = NULL;
+ current->futex.robust_lists[FUTEX_ROBUST_LIST_COMPAT_IDX] = 0;
return false;
}
#else
@@ -1517,6 +1528,9 @@ bool futex_robust_list_clear_pending(void __user *pop, unsigned int flags)
{
bool size32bit = !!(flags & FLAGS_ROBUST_LIST32);
+ if (!current->futex.robust_lists)
+ return false;
+
if (!IS_ENABLED(CONFIG_64BIT) && !size32bit)
return false;
@@ -1581,18 +1595,6 @@ static void exit_robust_lists(struct task_struct *tsk)
static void futex_cleanup(struct task_struct *tsk)
{
- if (unlikely(tsk->futex.robust_list)) {
- exit_robust_list(tsk, tsk->futex.robust_list);
- tsk->futex.robust_list = NULL;
- }
-
-#ifdef CONFIG_64BIT
- if (unlikely(tsk->futex.robust_list32)) {
- exit_robust_list32(tsk, tsk->futex.robust_list32);
- tsk->futex.robust_list32 = NULL;
- }
-#endif
-
if (unlikely(tsk->futex.robust_lists))
exit_robust_lists(tsk);
diff --git a/kernel/futex/syscalls.c b/kernel/futex/syscalls.c
index 596204870f65..f5ee8dd17414 100644
--- a/kernel/futex/syscalls.c
+++ b/kernel/futex/syscalls.c
@@ -27,21 +27,19 @@
*/
SYSCALL_DEFINE2(set_robust_list, struct robust_list_head __user *, head, size_t, len)
{
- /* The kernel knows only one size for now. */
+ int ret;
+
+ /*
+ * The kernel knows only one size for now:
+ */
if (unlikely(len != sizeof(*head)))
return -EINVAL;
- current->futex.robust_list = head;
- return 0;
-}
-
-static inline void __user *futex_task_robust_list(struct task_struct *p, bool compat)
-{
-#ifdef CONFIG_COMPAT
- if (compat)
- return p->futex.robust_list32;
-#endif
- return p->futex.robust_list;
+ ret = futex_robust_list_create((uintptr_t) head,
+ FUTEX_ROBUST_LIST_SET_NATIVE);
+ if (ret >= 0)
+ return 0;
+ return ret;
}
static void __user *futex_get_robust_list_common(int pid, bool compat, int index)
@@ -50,6 +48,9 @@ static void __user *futex_get_robust_list_common(int pid, bool compat, int index
void __user *head;
int ret;
+ if (index >= FUTEX_ROBUST_LIST2_MAX_IDX)
+ return (void __user *)ERR_PTR(-EINVAL);
+
scoped_guard(rcu) {
if (pid) {
p = find_task_by_vpid(pid);
@@ -74,11 +75,8 @@ static void __user *futex_get_robust_list_common(int pid, bool compat, int index
if (index >= 0) {
scoped_guard(mutex, &p->futex.exit_mutex) {
uintptr_t *rl = p->futex.robust_lists;
-
head = rl ? (void __user *) rl[index] : NULL;
}
- } else {
- head = futex_task_robust_list(p, compat);
}
up_read(&p->signal->exec_update_lock);
@@ -103,7 +101,11 @@ SYSCALL_DEFINE3(get_robust_list, int, pid,
struct robust_list_head __user * __user *, head_ptr,
size_t __user *, len_ptr)
{
- struct robust_list_head __user *head = futex_get_robust_list_common(pid, false, -1);
+ struct robust_list_head __user *head =
+ futex_get_robust_list_common(pid, false, FUTEX_ROBUST_LIST_NATIVE_IDX);
+
+ head = (struct robust_list_head __user *)
+ ((uintptr_t) head & FUTEX_ROBUST_LIST_ENTRY_MASK);
if (IS_ERR(head))
return PTR_ERR(head);
@@ -162,8 +164,7 @@ SYSCALL_DEFINE4(set_robust_list2, struct robust_list_head *, head, unsigned int,
return -EINVAL;
}
-SYSCALL_DEFINE4(get_robust_list2, int, pid,
- void __user * __user *, head_ptr,
+SYSCALL_DEFINE4(get_robust_list2, int, pid, void __user * __user *, head_ptr,
unsigned int, index, unsigned int, flags)
{
void __user *entry_ptr;
@@ -586,22 +587,23 @@ COMPAT_SYSCALL_DEFINE2(set_robust_list, struct robust_list_head32 __user *, head
if (unlikely(len != sizeof(*head)))
return -EINVAL;
- current->futex.robust_list32 = head;
-
- return 0;
+ return futex_robust_list_create((uintptr_t) head,
+ FUTEX_ROBUST_LIST_SET_COMPAT);
}
COMPAT_SYSCALL_DEFINE3(get_robust_list, int, pid,
compat_uptr_t __user *, head_ptr,
compat_size_t __user *, len_ptr)
{
- struct robust_list_head32 __user *head = futex_get_robust_list_common(pid, true, -1);
+ struct robust_list_head32 __user *head =
+ futex_get_robust_list_common(pid, true, FUTEX_ROBUST_LIST_COMPAT_IDX);
- if (IS_ERR(head))
- return PTR_ERR(head);
+ head = (struct robust_list_head32 __user *)
+ ((uintptr_t) head & FUTEX_ROBUST_LIST_ENTRY_MASK);
if (put_user(sizeof(*head), len_ptr))
return -EFAULT;
+
return put_user(ptr_to_compat(head), head_ptr);
}
#endif /* CONFIG_COMPAT */
--
2.55.0