Cloudanix VulnDB

CVE-2026-54905

Medium · 5.5

CVSS

5.5

EPSS

0.2%

Published

Jun 24, 2026

CISA due date

—

Cloudanix analysis

Next patch cycle SSVC: Track

A common Ruby software component can mishandle heavy repeated locking, potentially letting applications make unsafe simultaneous changes and produce incorrect results.

Chainable
Exploitation
Local Privilege
Attacker needs
Low Privilege
Complexity
Moderate
Impact
Data Tampering
Component
Library
Class
Race Condition
Remediation
Upgrade
Effort
Medium
Patch risk
Low

What it actually does

CVE-2026-54905 is a local, application-level race-condition flaw in concurrent-ruby before version 1.3.7 affecting Concurrent::ReentrantReadWriteLock. The lock encodes per-thread read and write hold state in a single integer: the lower 15 bits track reentrant read-lock count, while bit 15 indicates WRITE_LOCK_HELD. If a thread acquires the same read lock 32,768 times, the read count overflows into the write-lock flag. A subsequent try_write_lock call can then incorrectly return success because the thread appears to already hold the write lock, even though the global writer state is not set. Code relying on the lock may perform write-side operations while other threads still hold or acquire read locks, violating mutual exclusion and potentially causing data corruption, inconsistent reads, lost updates, or application instability.

Why it matters to the business

Applications using affected versions of concurrent-ruby may make unsafe concurrent updates under rare but reachable conditions, especially in highly concurrent Ruby services or code paths that allow repeated reentrant read locking. The likely business impact is integrity and reliability risk rather than direct remote compromise: incorrect application state, corrupted in-memory data structures, failed transactions, intermittent errors, or hard-to-reproduce production defects. The vulnerability is not in CISA KEV, has very low EPSS, and has no known public exploit maturity reported, so emergency internet-wide exploitation is unlikely; however, because concurrent-ruby is a common dependency, organizations should still prioritize dependency upgrades through normal patch cycles, particularly for systems where correctness of shared state is business-critical.

How an attack unfolds

An attacker would need the ability to influence execution inside a Ruby process that uses Concurrent::ReentrantReadWriteLock in a vulnerable pattern. This generally requires local code execution, plugin/script execution, compromised application logic, or a feature that allows an authenticated user to drive the application into repeatedly acquiring the same read lock on one thread. The attacker causes or triggers 32,768 reentrant read-lock acquisitions by the same thread, which flips the bit used internally to represent write-lock ownership. The attacker then causes code to call try_write_lock. The lock incorrectly reports that the write lock was acquired, but it does not set the global writer marker. Application code may then perform write operations while readers continue concurrently, creating inconsistent or corrupted state. Practical exploitation depends heavily on how the application uses this lock and whether an attacker can repeatedly enter the affected locking path.

How to fix it

Upgrade concurrent-ruby to version 1.3.7 or later wherever it is used. For Ruby applications managed with Bundler, update the Gemfile or dependency constraints if necessary and run `bundle update concurrent-ruby`, then verify that Gemfile.lock resolves concurrent-ruby to 1.3.7 or newer. For libraries or packaged applications, rebuild and redeploy artifacts after dependency resolution so the fixed gem is actually present at runtime. If immediate upgrade is not possible, review code paths using Concurrent::ReentrantReadWriteLock and avoid patterns that can recursively acquire the same read lock thousands of times; add defensive limits around recursion/reentrancy; reduce attacker influence over code paths that repeatedly acquire locks; and consider replacing this lock with an alternative synchronization primitive for sensitive shared-state updates until patched.

How to confirm the fix worked

Confirm that all deployed Ruby application artifacts resolve concurrent-ruby to 1.3.7 or later. Use dependency lockfiles, SBOMs, container image scans, and runtime gem inspection to verify the active version, not only the source repository constraint. Examples include checking `Gemfile.lock` for `concurrent-ruby (1.3.7)` or newer, running `bundle info concurrent-ruby`, or querying the runtime with `ruby -e 'require "concurrent-ruby"; puts Concurrent::VERSION'`. Where the lock is used in critical code, run concurrency/regression tests that exercise read/write lock behavior under high reentrant read counts and confirm write paths do not execute concurrently with active readers. Validate that rebuilt containers, packages, serverless bundles, and vendored gems no longer contain concurrent-ruby versions prior to 1.3.7.

Typical attack path

  1. Identify or gain access to a Ruby application using concurrent-ruby before 1.3.7 and Concurrent::ReentrantReadWriteLock.
  2. Find an application path, extension point, job, plugin, or API behavior that repeatedly reacquires the same read lock on the same thread.
  3. Trigger at least 32,768 reentrant read-lock acquisitions so the local read-hold counter crosses into the bit used to mark write-lock ownership.
  4. Trigger a try_write_lock operation from the same thread.
  5. Abuse the false write-lock success to cause write-side logic to execute while other threads can still read or acquire read locks.
  6. Leverage the resulting race condition to cause inconsistent application state, lost updates, corrupted in-memory structures, or service instability.

How to detect it

There are no universal network indicators for this vulnerability because exploitation is local to application logic and depends on how a Ruby process uses Concurrent::ReentrantReadWriteLock. To determine prior exploitation or impact, review application logs, error tracking, APM traces, and audit logs for unusual bursts of requests, jobs, scripts, or plugin activity followed by data inconsistency, lost updates, cache corruption, unexplained state transitions, or concurrency-related exceptions. Inspect application code and traces for repeated or recursive read-lock acquisition paths using Concurrent::ReentrantReadWriteLock, especially paths reachable by authenticated users, background jobs, custom automation, or untrusted extensions. On hosts or containers, search deployed gems and lockfiles for concurrent-ruby versions earlier than 1.3.7 to establish exposure windows. If detailed Ruby tracing or custom lock instrumentation exists, look for extremely high per-thread reentrant read-lock counts, repeated read-lock acquisition loops, and try_write_lock successes occurring without expected writer exclusion. Correlate suspicious behavior with deploy timelines and dependency versions to distinguish exploitation from ordinary race-condition defects.

  • concurrent-ruby (1.3.6)
  • concurrent-ruby (1.3.5)
  • concurrent-ruby (1.3.4)
  • concurrent-ruby (1.3.3)
  • concurrent-ruby (1.3.2)
  • concurrent-ruby (1.3.1)
  • concurrent-ruby (1.3.0)
  • Concurrent::ReentrantReadWriteLock
  • try_write_lock
  • WRITE_LOCK_HELD
  • RUNNING_WRITER
  • Gemfile.lock
  • /usr/local/bundle/gems/concurrent-ruby-1.3.6
  • /usr/local/bundle/gems/concurrent-ruby-1.3.5
  • /usr/local/bundle/gems/concurrent-ruby-1.3.4

What your security team should do

  • Inventory applications, containers, CI artifacts, and vendored Ruby dependencies for concurrent-ruby versions earlier than 1.3.7.
  • Prioritize patching systems where concurrent-ruby protects shared business-critical state, caches, authorization decisions, financial/accounting logic, workflow state, or persistent data updates.
  • Update dependency constraints and lockfiles, rebuild deployable artifacts, and redeploy so runtime environments use concurrent-ruby 1.3.7 or later.
  • Add software composition analysis checks to block introduction of concurrent-ruby versions prior to 1.3.7 in new builds.
  • Ask development teams to review uses of Concurrent::ReentrantReadWriteLock for extreme reentrancy, recursive read-lock acquisition, or attacker-influenced loops.
  • If patching is delayed, add application-level guardrails to prevent unbounded recursive/reentrant locking and monitor for unusual error rates, state consistency issues, deadlocks, or race-related exceptions.
  • Because this issue is not KEV-listed, has very low EPSS, and has no known exploit maturity, handle as a dependency integrity/reliability fix rather than an emergency perimeter incident unless local code execution or untrusted plugin execution is present.

The record

concurrent-ruby is a modern concurrency tools for Ruby. Prior to 1.3.7, 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 a write lock and returns true without setting the global RUNNING_WRITER bit. This breaks the core mutual-exclusion guarantee: the caller is told it has a write lock, but other threads can still hold or acquire read locks at the same time. This vulnerability is fixed in 1.3.7.

Affected packages

Ecosystem Package Introduced Fixed

Related vulnerabilities