CVE-2026-54906: concurrent-ruby: ReadWriteLock allows wrong-thread write release and stray read-release counter corruption

Published Jun 19, 2026
·
Updated

Summary Concurrent::ReadWriteLock#releasewritelock 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#releasereadlock also decrements the shared counter even when no read lock is held. Calling it on a fresh lock changes the counter from 0 to -1, after which normal read acquisition raises Concurrent::ResourceLimitError.

This is a synchronization correctness issue in the public Concurrent::ReadWriteLock API. It should not be framed as an authorization bypass; the lock is an in-process concurrency primitive, not an access-control boundary.

Version Software: concurrent-ruby Version: 1.3.6 Commit: 7a1b78941c081106c20a9ca0144ac73a48d254ab

Details

releasewritelock checks only whether the global counter indicates that a writer is running. It does not track or verify ownership:

ruby def releasewritelock return true unless runningwriter? c = @Counter.update { |counter| counter - RUNNINGWRITER } @ReadLock.broadcast @WriteLock.signal if waitingwriters(c) > 0 true end

Because ownership is not checked, a different thread can clear the RUNNINGWRITER bit while the original writer is still inside its critical section. Another writer can then acquire the write lock and run concurrently with the first writer.

releasereadlock unconditionally decrements the shared counter:

ruby def releasereadlock while true c = @Counter.value if @Counter.compareandset(c, c-1) if waitingwriter?(c) && runningreaders(c) == 1 @WriteLock.signal end break end end true end

On a fresh lock, this changes the counter from 0 to -1. A later acquirereadlock raises Concurrent::ResourceLimitError because the maximum-reader check masks the negative counter as saturated.

Reproduce

From the root of a concurrent-ruby checkout, run:

bash ruby -Ilib/concurrent-ruby - <<'RUBY' require 'concurrent/atomic/readwritelock' require 'concurrent/version' require 'thread'

puts "ruby=#{RUBYDESCRIPTION}" puts "concurrentrubyversion=#{Concurrent::VERSION}" puts "poc=ReadWriteLock release methods corrupt or bypass lock state"

lock = Concurrent::ReadWriteLock.new events = Queue.new writer1inside = false

writer1 = Thread.new do lock.acquirewritelock writer1inside = true events << :writer1acquired sleep 0.5 writer1inside = false lock.releasewritelock events << :writer1finished end

events.pop puts 'writer1acquired=true'

intruderresult = nil intruder = Thread.new do intruderresult = lock.releasewritelock end intruder.join

puts "wrongthreadreleasewritelockreturned=#{intruderresult}"

writer2enteredwhilewriter1inside = nil writer2 = Thread.new do lock.acquirewritelock writer2enteredwhilewriter1inside = writer1inside lock.releasewritelock end

writer2.join(0.25)

puts "writer2acquiredwhilewriter1inside=#{writer2enteredwhilewriter1inside}"

writer1.join

lock2 = Concurrent::ReadWriteLock.new strayreadreleaseresult = lock2.releasereadlock counterafterstrayreadrelease = lock2.instanceeval { @Counter.value } readafterstrayrelease = begin lock2.acquirereadlock 'acquired' rescue => error "#{error.class}: #{error.message}" end

puts "strayreleasereadlockreturned=#{strayreadreleaseresult}" puts "counterafterstrayreadrelease=#{counterafterstrayreadrelease}" puts "acquirereadafterstrayrelease=#{readafterstrayrelease}"

if intruderresult && writer2enteredwhilewriter1inside && counterafterstrayreadrelease == -1 puts 'result=REPRODUCED wrong-thread write release and stray read-release corruption' else puts 'result=NOTREPRODUCED' end Expected result:

- A second thread successfully calls releasewritelock while the first writer still holds the lock. - A second writer enters while the first writer is still inside the write critical section. - Calling releasereadlock on a fresh lock changes the counter to -1. - A subsequent read acquisition fails with Concurrent::ResourceLimitError.

Log evidence

Local reproduction output:

text ruby=ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25] concurrentrubyversion=1.3.6 poc=ReadWriteLock release methods corrupt or bypass lock state writer1acquired=true wrongthreadreleasewritelockreturned=true writer2acquiredwhilewriter1inside=true strayreleasereadlockreturned=true counterafterstrayreadrelease=-1 acquirereadafterstrayrelease=Concurrent::ResourceLimitError: Too many reader threads result=REPRODUCED wrong-thread write release and stray read-release corruption

Impact This can break the write-lock mutual exclusion guarantee and can also leave a lock unusable after a stray read release. The impact is local to applications that expose or misuse the manual acquire / release APIs. If the lock protects integrity-sensitive mutable state, wrong-thread write release can allow concurrent writers and data races. The stray read-release path can cause denial of service by corrupting the lock counter.

Credit Pranjali Thakur - depthfirst (depthfirst.com)

Other sources

concurrent-ruby is a modern concurrency tools for Ruby. Prior to 1.3.7, Concurrent::ReadWriteLock#releasewritelock 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#releasereadlock also decrements the shared counter even when no read lock is held. Calling it on a fresh lock changes the counter from 0 to -1, after which normal read acquisition raises Concurrent::ResourceLimitError. This is a synchronization correctness issue in the public Concurrent::ReadWriteLock API. This vulnerability is fixed in 1.3.7.

— MITRE

concurrent-ruby: ReadWriteLock allows wrong-thread write release and stray read-release counter corruption

— Microsoft

Affected Software

4 affected componentsFixes available
rubygems/concurrent-ruby<1.3.7
1.3.7
Rubyconcurrency Concurrent Ruby Ruby<1.3.7
Microsoft azl3 rubygem-concurrent-ruby 1.2.2-1<1.3.7-1
1.3.7-1
IBM API Connect V12 OnPrem<=v12.1.0.0 - v12.1.1.2

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade rubygems/concurrent-ruby to a version that resolves this vulnerability.

    Fixed in 1.3.7
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 1.3.7-1
  3. Upgrade

    Upgrade concurrent-ruby/Concurrent::ReadWriteLock to a version that resolves this vulnerability.

    Fixed in 1.3.7

Event History

Jun 19, 2026
Advisory Published
via GitHub·08:47 PM
Data Sourced
via GitHub·08:47 PM
DescriptionWeaknessAffected Software
Jun 24, 2026
CVE Published
via MITRE·03:46 PM
Data Sourced
via MITRE·03:46 PM
DescriptionWeakness
Data Sourced
via NVD·05:17 PM
DescriptionSeverityWeaknessAffected Software
Jun 27, 2026
Data Sourced
via Microsoft·08:09 AM
DescriptionSeverityWeaknessAffected Software
Oct 8, 2026
Data Sourced
via IBM·12:00 AM
DescriptionAffected Software

Parent advisories

This vulnerability appears in the following advisories.

Frequently Asked Questions

1

What is the severity of CVE-2026-54906?

The severity of CVE-2026-54906 is rated as 40.

2

What is the main issue in CVE-2026-54906?

CVE-2026-54906 allows any thread to release an active write lock held by another thread, leading to potential data corruption.

3

How do I fix CVE-2026-54906?

To fix CVE-2026-54906, ensure that the write lock is properly verified by the calling thread before it is released.

4

Which software is affected by CVE-2026-54906?

CVE-2026-54906 affects the concurrent-ruby gem in RubyGems.

5

When was CVE-2026-54906 published?

CVE-2026-54906 was published on June 19, 2026.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203