On Thu, Feb 16, 2023 at 04:09:32PM -0500, Waiman Long wrote:Right, I should keep the clearing after deleting the waiter. Thanks for spotting that. I will review your patches soon.
@@ -432,19 +433,39 @@ static void rwsem_mark_wake(struct rw_semaphore *sem,*sigh*... you can't even properly copy/paste from the reader side :/
* Mark writer at the front of the queue for wakeup.
* Until the task is actually later awoken later by
* the caller, other writers are able to steal it.
+ *
+ * *Unless* HANDOFF is set, in which case only the
+ * first waiter is allowed to take it.
+ *
* Readers, on the other hand, will block as they
* will notice the queued writer.
*/
wake_q_add(wake_q, waiter->task);
lockevent_inc(rwsem_wake_writer);
+
+ if ((count & RWSEM_LOCK_MASK) || !(count & RWSEM_FLAG_HANDOFF))
+ return;
+
+ /*
+ * If the rwsem is free and handoff flag is set with wait_lock
+ * held, no other CPUs can take an active lock. We can do an
+ * early handoff.
+ */
+ adjustment = RWSEM_WRITER_LOCKED - RWSEM_FLAG_HANDOFF;
+ atomic_long_set(&sem->owner, (long)waiter->task);
+ waiter->task = NULL;
+ atomic_long_add(adjustment, &sem->count);
+ rwsem_del_waiter(sem, waiter);
+ lockevent_inc(rwsem_wlock_ehandoff);
}
This is broken, the moment you do waiter->task = NULL, waiter can
dissapear from under you and that rwsem_del_waiter() is a UaF.
Nor did you unify the reader and writer side and still have a giant
trainwreck on your hands..
Surely all that isn't too hard... let me post it.