Advisories for Gem/Concurrent-Ruby package

2026

Concurrent Ruby: ReadWriteLock allows wrong-thread write release and stray read-release counter corruption

Concurrent::ReadWriteLock#release_write_lock does not verify that the calling thread acquired the write lock. Any thread with access to the lock object can release an active write lock held by another thread. A second writer can then enter its critical section while the first writer is still running. Concurrent::ReadWriteLock#release_read_lock also decrements the shared counter even when no read lock is held. Calling it on a fresh lock changes the counter from 0 …

Concurrent Ruby: `ReentrantReadWriteLock` read-count overflow grants a write lock without exclusivity

Concurrent::ReentrantReadWriteLock can incorrectly grant a write lock after one thread acquires the read lock 32,768 times. The lock stores a thread's local read and write hold counts in one integer. The low 15 bits are used for the read hold count, and bit 15 is used as WRITE_LOCK_HELD. After 32,768 reentrant read acquisitions, the local read count crosses into the write-lock bit. try_write_lock then treats the thread as already holding …

Concurrent Ruby : `AtomicReference#update` livelocks when the stored value is `Float::NAN`

Concurrent::AtomicReference#update can enter a permanent busy retry loop when the current value is Float::NAN. The issue is caused by the interaction between: AtomicReference#update, which retries until compare_and_set(old_value, new_value) succeeds. Numeric compare_and_set, which checks old == old_value before attempting the underlying atomic swap. Ruby NaN semantics, where Float::NAN == Float::NAN is always false. As a result, once an AtomicReference contains Float::NAN, calling #update repeatedly evaluates the caller's block and never returns. …