git: b59cf25eac06 - main - linux(4): Handle special case for regular futex in handle_futex_death().

Dmitry Chagin dchagin at FreeBSD.org
Thu Jul 29 09:58:50 UTC 2021


The branch main has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=b59cf25eac06c1feb2d35a5a3d8a089ae62fd0df

commit b59cf25eac06c1feb2d35a5a3d8a089ae62fd0df
Author:     Dmitry Chagin <dchagin at FreeBSD.org>
AuthorDate: 2021-07-29 09:51:39 +0000
Commit:     Dmitry Chagin <dchagin at FreeBSD.org>
CommitDate: 2021-07-29 09:51:39 +0000

    linux(4): Handle special case for regular futex in handle_futex_death().
    
    Handle some races in handle_futex_death() which can prevents a wakeup of
    potential waiters which can cause these waiters to block forever.
    
    Differential Revision:  https://reviews.freebsd.org/D31280
    MFC after:              2 weeks
---
 sys/compat/linux/linux_futex.c | 54 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 9 deletions(-)

diff --git a/sys/compat/linux/linux_futex.c b/sys/compat/linux/linux_futex.c
index c81fe20212a2..8bc8879c92c6 100644
--- a/sys/compat/linux/linux_futex.c
+++ b/sys/compat/linux/linux_futex.c
@@ -108,7 +108,7 @@ LIN_SDT_PROBE_DEFINE1(futex, release_futexes, copyin_error, "int");
 
 static int futex_atomic_op(struct thread *, int, uint32_t *);
 static int handle_futex_death(struct thread *td, struct linux_emuldata *,
-    uint32_t *, unsigned int);
+    uint32_t *, unsigned int, bool);
 static int fetch_robust_entry(struct linux_robust_list **,
     struct linux_robust_list **, unsigned int *);
 
@@ -995,7 +995,7 @@ linux_get_robust_list(struct thread *td, struct linux_get_robust_list_args *args
 
 static int
 handle_futex_death(struct thread *td, struct linux_emuldata *em, uint32_t *uaddr,
-    unsigned int pi)
+    unsigned int pi, bool pending_op)
 {
 	uint32_t uval, nval, mval;
 	int error;
@@ -1004,6 +1004,31 @@ retry:
 	error = fueword32(uaddr, &uval);
 	if (error != 0)
 		return (EFAULT);
+
+	/*
+	 * Special case for regular (non PI) futexes. The unlock path in
+	 * user space has two race scenarios:
+	 *
+	 * 1. The unlock path releases the user space futex value and
+	 *    before it can execute the futex() syscall to wake up
+	 *    waiters it is killed.
+	 *
+	 * 2. A woken up waiter is killed before it can acquire the
+	 *    futex in user space.
+	 *
+	 * In both cases the TID validation below prevents a wakeup of
+	 * potential waiters which can cause these waiters to block
+	 * forever.
+	 *
+	 * In both cases it is safe to attempt waking up a potential
+	 * waiter without touching the user space futex value and trying
+	 * to set the OWNER_DIED bit.
+	 */
+	if (pending_op && !pi && !uval) {
+		(void)futex_wake(td, uaddr, 1, true);
+		return (0);
+	}
+
 	if ((uval & FUTEX_TID_MASK) == em->em_tid) {
 		mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
 		error = casueword32(uaddr, uval, &nval, mval);
@@ -1049,6 +1074,9 @@ fetch_robust_entry(struct linux_robust_list **entry,
 	return (0);
 }
 
+#define	LINUX_HANDLE_DEATH_PENDING	true
+#define	LINUX_HANDLE_DEATH_LIST		false
+
 /* This walks the list of robust futexes releasing them. */
 void
 release_futexes(struct thread *td, struct linux_emuldata *em)
@@ -1056,6 +1084,7 @@ release_futexes(struct thread *td, struct linux_emuldata *em)
 	struct linux_robust_list_head *head = NULL;
 	struct linux_robust_list *entry, *next_entry, *pending;
 	unsigned int limit = 2048, pi, next_pi, pip;
+	uint32_t *uaddr;
 	l_long futex_offset;
 	int rc, error;
 
@@ -1080,11 +1109,16 @@ release_futexes(struct thread *td, struct linux_emuldata *em)
 	while (entry != &head->list) {
 		rc = fetch_robust_entry(&next_entry, PTRIN(&entry->next), &next_pi);
 
-		if (entry != pending)
-			if (handle_futex_death(td, em,
-			    (uint32_t *)((caddr_t)entry + futex_offset), pi)) {
+		/*
+		 * A pending lock might already be on the list, so
+		 * don't process it twice.
+		 */
+		if (entry != pending) {
+			uaddr = (uint32_t *)((caddr_t)entry + futex_offset);
+			if (handle_futex_death(td, em, uaddr, pi,
+			    LINUX_HANDLE_DEATH_LIST))
 				return;
-			}
+		}
 		if (rc)
 			return;
 
@@ -1097,7 +1131,9 @@ release_futexes(struct thread *td, struct linux_emuldata *em)
 		sched_relinquish(curthread);
 	}
 
-	if (pending)
-		handle_futex_death(td, em,
-		    (uint32_t *)((caddr_t)pending + futex_offset), pip);
+	if (pending) {
+		uaddr = (uint32_t *)((caddr_t)pending + futex_offset);
+		(void)handle_futex_death(td, em, uaddr, pip,
+		    LINUX_HANDLE_DEATH_PENDING);
+	}
 }


More information about the dev-commits-src-all mailing list