[RFC PATCH v2] um: fix futex implementation
Anton Ivanov
anton.ivanov at cambridgegreys.com
Tue Jul 21 02:40:37 PDT 2026
On 21/07/2026 09:43, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg at intel.com>
>
> The futex operations are both now wrong, neither have proper
> atomic semantics, which are needed for SMP. Even if we don't
> have SMP across threads of a process yet, multiple processes
> can use futexes to synchronize.
>
> Also remove the documentation that's duplicated (and wrong,
> as this implementation never returns -EAGAIN).
>
> Signed-off-by: Johannes Berg <johannes.berg at intel.com>
> ---
> v2: fix oldval read
> ---
> arch/um/kernel/skas/uaccess.c | 134 +++++++++++++---------------------
> 1 file changed, 51 insertions(+), 83 deletions(-)
>
> diff --git a/arch/um/kernel/skas/uaccess.c b/arch/um/kernel/skas/uaccess.c
> index caef1deef795..f48f0e7c2497 100644
> --- a/arch/um/kernel/skas/uaccess.c
> +++ b/arch/um/kernel/skas/uaccess.c
> @@ -231,116 +231,90 @@ long strnlen_user(const char __user *str, long len)
> }
> EXPORT_SYMBOL(strnlen_user);
>
> -/**
> - * arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
> - * argument and comparison of the previous
> - * futex value with another constant.
> - *
> - * @op: operation to execute
> - * @oparg: argument to operation
> - * @oval: old value at uaddr
> - * @uaddr: pointer to user space address
> - *
> - * Return:
> - * 0 - On success
> - * -EFAULT - User access resulted in a page fault
> - * -EAGAIN - Atomic operation was unable to complete due to contention
> - * -ENOSYS - Operation not supported
> - */
> -
> int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
> {
> - int oldval, ret;
> - struct page *page;
> unsigned long addr = (unsigned long) uaddr;
> + u32 *p, oldval, newval;
> + struct page *page;
> pte_t *pte;
>
> - ret = -EFAULT;
> if (!access_ok(uaddr, sizeof(*uaddr)))
> return -EFAULT;
> +
> + switch (op) {
> + case FUTEX_OP_SET:
> + case FUTEX_OP_ADD:
> + case FUTEX_OP_OR:
> + case FUTEX_OP_ANDN:
> + case FUTEX_OP_XOR:
> + break;
> + default:
> + return -ENOSYS;
> + }
> +
> preempt_disable();
> pte = maybe_map(addr, 1);
> - if (pte == NULL)
> - goto out_inuser;
> + if (pte == NULL) {
> + preempt_enable();
> + return -EFAULT;
> + }
>
> page = pte_page(*pte);
> #ifdef CONFIG_64BIT
> pagefault_disable();
> - addr = (unsigned long) page_address(page) +
> - (((unsigned long) addr) & ~PAGE_MASK);
> + p = page_address(page) + (addr & ~PAGE_MASK);
> #else
> - addr = (unsigned long) kmap_atomic(page) +
> - ((unsigned long) addr & ~PAGE_MASK);
> + p = kmap_atomic(page) + (addr & ~PAGE_MASK);
> #endif
> - uaddr = (u32 *) addr;
> - oldval = *uaddr;
>
> - ret = 0;
> + do {
> + oldval = READ_ONCE(*p);
> +
> + switch (op) {
> + case FUTEX_OP_SET:
> + newval = oparg;
> + break;
> + case FUTEX_OP_ADD:
> + newval = oldval + oparg;
> + break;
> + case FUTEX_OP_OR:
> + newval = oldval | oparg;
> + break;
> + case FUTEX_OP_ANDN:
> + newval = oldval & ~oparg;
> + break;
> + case FUTEX_OP_XOR:
> + newval = oldval ^ oparg;
> + break;
> + }
> + } while (!try_cmpxchg(p, &oldval, newval));
> +
> + *oval = oldval;
>
> - switch (op) {
> - case FUTEX_OP_SET:
> - *uaddr = oparg;
> - break;
> - case FUTEX_OP_ADD:
> - *uaddr += oparg;
> - break;
> - case FUTEX_OP_OR:
> - *uaddr |= oparg;
> - break;
> - case FUTEX_OP_ANDN:
> - *uaddr &= ~oparg;
> - break;
> - case FUTEX_OP_XOR:
> - *uaddr ^= oparg;
> - break;
> - default:
> - ret = -ENOSYS;
> - }
> #ifdef CONFIG_64BIT
> pagefault_enable();
> #else
> kunmap_atomic((void *)addr);
> #endif
> -
> -out_inuser:
> preempt_enable();
> -
> - if (ret == 0)
> - *oval = oldval;
> -
> - return ret;
> + return 0;
> }
> -EXPORT_SYMBOL(arch_futex_atomic_op_inuser);
> -
> -/**
> - * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
> - * uaddr with newval if the current value is
> - * oldval.
> - * @uval: pointer to store content of @uaddr
> - * @uaddr: pointer to user space address
> - * @oldval: old value
> - * @newval: new value to store to @uaddr
> - *
> - * Return:
> - * 0 - On success
> - * -EFAULT - User access resulted in a page fault
> - * -EAGAIN - Atomic operation was unable to complete due to contention
> - */
>
> int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
> - u32 oldval, u32 newval)
> + u32 oldval, u32 newval)
> {
> struct page *page;
> pte_t *pte;
> - int ret = -EFAULT;
>
> if (!access_ok(uaddr, sizeof(*uaddr)))
> return -EFAULT;
>
> preempt_disable();
> pte = maybe_map((unsigned long) uaddr, 1);
> - if (pte == NULL)
> - goto out_inatomic;
> + if (pte == NULL) {
> + preempt_enable();
> + return -EFAULT;
> + }
>
> page = pte_page(*pte);
> #ifdef CONFIG_64BIT
> @@ -350,19 +324,13 @@ int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
> uaddr = kmap_atomic(page) + ((unsigned long) uaddr & ~PAGE_MASK);
> #endif
>
> - *uval = *uaddr;
> -
> - ret = cmpxchg(uaddr, oldval, newval);
> + *uval = cmpxchg(uaddr, oldval, newval);
>
> #ifdef CONFIG_64BIT
> pagefault_enable();
> #else
> kunmap_atomic(uaddr);
> #endif
> - ret = 0;
> -
> -out_inatomic:
> preempt_enable();
> - return ret;
> + return 0;
> }
> -EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);
Acked-By: Anton Ivanov <anton.ivanov at cambridgegreys.com>
--
Anton R. Ivanov
Cambridgegreys Limited. Registered in England. Company Number 10273661
More information about the linux-um
mailing list