https://github.com/python/cpython/commit/c32bae52904723d99e1f98e2ef54570268d86467
commit: c32bae52904723d99e1f98e2ef54570268d86467
branch: main
author: mpage <[email protected]>
committer: DinoV <[email protected]>
date: 2024-02-05T13:48:37-08:00
summary:

gh-114944: Fix race between `_PyParkingLot_Park` and `_PyParkingLot_UnparkAll` 
when handling interrupts (#114945)

Fix race between `_PyParkingLot_Park` and `_PyParkingLot_UnparkAll` when 
handling interrupts

There is a potential race when `_PyParkingLot_UnparkAll` is executing in
one thread and another thread is unblocked because of an interrupt in
`_PyParkingLot_Park`. Consider the following scenario:

1. Thread T0 is blocked[^1] in `_PyParkingLot_Park` on address `A`.
2. Thread T1 executes `_PyParkingLot_UnparkAll` on address `A`. It
   finds the `wait_entry` for `T0` and unlinks[^2] its list node.
3. Immediately after (2), T0 is woken up due to an interrupt. It
   then segfaults trying to unlink[^3] the node that was previously
   unlinked in (2).

To fix this we mark each waiter as unparking before releasing the bucket
lock. `_PyParkingLot_Park` will wait to handle the coming wakeup, and not
attempt to unlink the node, when this field is set. `_PyParkingLot_Unpark`
does this already, presumably to handle this case.

files:
A Misc/NEWS.d/next/Core and 
Builtins/2024-02-03-01-48-38.gh-issue-114944.4J5ELD.rst
M Python/parking_lot.c

diff --git a/Misc/NEWS.d/next/Core and 
Builtins/2024-02-03-01-48-38.gh-issue-114944.4J5ELD.rst b/Misc/NEWS.d/next/Core 
and Builtins/2024-02-03-01-48-38.gh-issue-114944.4J5ELD.rst
new file mode 100644
index 00000000000000..fb41caf7c5f4fa
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2024-02-03-01-48-38.gh-issue-114944.4J5ELD.rst 
@@ -0,0 +1 @@
+Fixes a race between ``PyParkingLot_Park`` and ``_PyParkingLot_UnparkAll``.
diff --git a/Python/parking_lot.c b/Python/parking_lot.c
index c83d7443e289c5..8ba50fc1353ebd 100644
--- a/Python/parking_lot.c
+++ b/Python/parking_lot.c
@@ -244,6 +244,7 @@ dequeue(Bucket *bucket, const void *address)
         if (wait->addr == (uintptr_t)address) {
             llist_remove(node);
             --bucket->num_waiters;
+            wait->is_unparking = true;
             return wait;
         }
     }
@@ -262,6 +263,7 @@ dequeue_all(Bucket *bucket, const void *address, struct 
llist_node *dst)
             llist_remove(node);
             llist_insert_tail(dst, node);
             --bucket->num_waiters;
+            wait->is_unparking = true;
         }
     }
 }
@@ -337,8 +339,6 @@ _PyParkingLot_Unpark(const void *addr, _Py_unpark_fn_t *fn, 
void *arg)
     _PyRawMutex_Lock(&bucket->mutex);
     struct wait_entry *waiter = dequeue(bucket, addr);
     if (waiter) {
-        waiter->is_unparking = true;
-
         int has_more_waiters = (bucket->num_waiters > 0);
         fn(arg, waiter->park_arg, has_more_waiters);
     }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to