From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (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 36AC556472 for ; Thu, 28 May 2026 01:09:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779930556; cv=none; b=IZ6QyC5RloBJe3aZCJjBVoF7MnZCJ/BemaauDtxfbddgc0cW5pqg7jIuofhPfcxxZXsfPWnfysQwbdATroDl3kWGsEAukDaSQ2sUT/mWn0J71YyLw5w7PW6UnMUTNd9nH2Q1glH2CYQpWo3xRtqMOM0/B1just3R5QI8Qesah8w= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779930556; c=relaxed/simple; bh=I1prj7PYebReUZLxzUatDNmLWiHoXLeo+XlJuxA9ahs=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=lGkDEA/uE27wuEQELsg+zEPMOFrsAZhHu1U8pm3h9UTpdeGEcsUZmG5e+r2FSgHTTWPGHm8y8l4OvzdCcepi4bJhcWVyvbqfSP91mT1YfV2GKaQ8OxdnQ6uLN69fFFo+NyKNd49qsIRiauP/dvIZdzDXksLFjcFLgUFZ/sSK+58= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=lAhb6TIy; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="lAhb6TIy" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:Content-Type:In-Reply-To:From: References:Cc:To:Subject:MIME-Version:Date:Message-ID:Sender:Reply-To: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=FqfJm/p38LzxW6batNY/OQTZ9vD/kI3Ir1OnQBEjiQ8=; b=lAhb6TIyreZK9IPGoEFB3DVAqC iXAbOcnPCP5qLQanDLbITp8nwjcYsA/Pz8uIPpABlnybPWlNdK3nhNJmAXPJQpfcVsyr5c6zBMy2X WfxioPmPNDh45lX+gmN/+wbP3AzxTDhBjUGmn/ieGAAy0+C+B+vrCzK0Nm1qYUj5jecYv1Rf01j/z Sb5asrcQRFfnfIrVgvVmgwvIEiZcTr16kJVHjL/Bh6KmBAYg7Ay77ndCfjPqVa1sRPHiCtR4CRCQP IWHQnvgDE8Sw75h8HZfXsGFxj8WS96AzqqpluV3fOmwt6xbvzMjculhOPTUT60k545ATH3KmvkVHn kcOhmf5g==; Received: from [179.118.191.12] (helo=[192.168.15.100]) by fanzine2.igalia.com with esmtpsa (Cipher TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_128_GCM:128) (Exim) id 1wSPF7-0099JT-M2; Thu, 28 May 2026 03:09:01 +0200 Message-ID: <9d6149b3-d153-4aba-ac66-38ce6b8d8e16@igalia.com> Date: Wed, 27 May 2026 22:08:54 -0300 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [patch V4 10/14] futex: Provide infrastructure to plug the non contended robust futex unlock race To: Thomas Gleixner Cc: Mathieu Desnoyers , Sebastian Andrzej Siewior , Carlos O'Donell , Peter Zijlstra , Florian Weimer , Rich Felker , Torvald Riegel , Darren Hart , Ingo Molnar , LKML , Davidlohr Bueso , Arnd Bergmann , "Liam R . Howlett" , Uros Bizjak , =?UTF-8?Q?Thomas_Wei=C3=9Fschuh?= References: <20260402151131.876492985@kernel.org> <20260402151940.282021578@kernel.org> Content-Language: en-US From: =?UTF-8?Q?Andr=C3=A9_Almeida?= In-Reply-To: <20260402151940.282021578@kernel.org> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Em 02/04/2026 12:21, Thomas Gleixner escreveu: > When the FUTEX_ROBUST_UNLOCK mechanism is used for unlocking (PI-)futexes, > then the unlock sequence in user space looks like this: > > 1) robust_list_set_op_pending(mutex); > 2) robust_list_remove(mutex); > > lval = gettid(); > 3) if (atomic_try_cmpxchg(&mutex->lock, lval, 0)) > 4) robust_list_clear_op_pending(); > else > 5) sys_futex(OP | FUTEX_ROBUST_UNLOCK, ....); > > That still leaves a minimal race window between #3 and #4 where the mutex > could be acquired by some other task, which observes that it is the last > user and: > > 1) unmaps the mutex memory > 2) maps a different file, which ends up covering the same address > > When then the original task exits before reaching #5 then the kernel robust > list handling observes the pending op entry and tries to fix up user space. > > In case that the newly mapped data contains the TID of the exiting thread > at the address of the mutex/futex the kernel will set the owner died bit in > that memory and therefore corrupt unrelated data. > > On X86 this boils down to this simplified assembly sequence: > > mov %esi,%eax // Load TID into EAX > xor %ecx,%ecx // Set ECX to 0 > #3 lock cmpxchg %ecx,(%rdi) // Try the TID -> 0 transition > .Lstart: > jnz .Lend > #4 movq %rcx,(%rdx) // Clear list_op_pending > .Lend: > > If the cmpxchg() succeeds and the task is interrupted before it can clear > list_op_pending in the robust list head (#4) and the task crashes in a > signal handler or gets killed then it ends up in do_exit() and subsequently > in the robust list handling, which then might run into the unmap/map issue > described above. > > This is only relevant when user space was interrupted and a signal is > pending. The fix-up has to be done before signal delivery is attempted > because: > > 1) The signal might be fatal so get_signal() ends up in do_exit() > > 2) The signal handler might crash or the task is killed before returning > from the handler. At that point the instruction pointer in pt_regs is > not longer the instruction pointer of the initially interrupted unlock > sequence. > > The right place to handle this is in __exit_to_user_mode_loop() before > invoking arch_do_signal_or_restart() as this covers obviously both > scenarios. > > As this is only relevant when the task was interrupted in user space, this > is tied to RSEQ and the generic entry code as RSEQ keeps track of user > space interrupts unconditionally even if the task does not have a RSEQ > region installed. That makes the decision very lightweight: > > if (current->rseq.user_irq && within(regs, csr->unlock_ip_range)) > futex_fixup_robust_unlock(regs, csr); > > futex_fixup_robust_unlock() then invokes a architecture specific function > to returen the pending op pointer or NULL. The function evaluates the > register content to decide whether the pending ops pointer in the robust > list head needs to be cleared. > > Assuming the above unlock sequence, then on x86 this decision is the > trivial evaluation of the zero flag: > > return regs->eflags & X86_EFLAGS_ZF ? regs->dx : NULL; > > Other architectures might need to do more complex evaluations due to LLSC, > but the approach is valid in general. The size of the pointer is determined > from the matching range struct, which covers both 32-bit and 64-bit builds > including COMPAT. > > The unlock sequence is going to be placed in the VDSO so that the kernel > can keep everything synchronized, especially the register usage. The > resulting code sequence for user space is: > > if (__vdso_futex_robust_list$SZ_try_unlock(lock, tid, &pending_op) != tid) > err = sys_futex($OP | FUTEX_ROBUST_UNLOCK,....); > > Both the VDSO unlock and the kernel side unlock ensure that the pending_op > pointer is always cleared when the lock becomes unlocked. > > Signed-off-by: Thomas Gleixner Reviewed-by: André Almeida See my comment bellow. [...] > + > +static inline void futex_fixup_robust_unlock(struct pt_regs *regs) > +{ > + struct futex_unlock_cs_range *csr; > + > + /* > + * Avoid dereferencing current->mm if not returning from interrupt. > + * current->rseq.event is going to be used subsequently, so bringing the > + * cache line in is not a big deal. > + */ > + if (!current->rseq.event.user_irq) > + return; > + For some reason, this if is always true for aarch64. I had to remove it in order to make the test work. Any idea why? > + csr = current->mm->futex.unlock.cs_ranges; > + > + /* The loop is optimized out for !COMPAT */ > + for (int r = 0; r < FUTEX_ROBUST_MAX_CS_RANGES; r++, csr++) { > + if (unlikely(futex_within_robust_unlock(regs, csr))) { > + __futex_fixup_robust_unlock(regs, csr); > + return; > + } > + } > +} >