JRuby 9.2.13 updates YAML parsing and repairs partial writes
JRuby 9.2.13.0 updates Psych and SnakeYAML, fixes incomplete IO.copy_stream writes and makes runtime optimization state safer across threads.
A Ruby runtime on the JVM inherits a dependency graph that is not fully described by a Gemfile. Its YAML implementation can include a Java parser, and its I/O operations must translate between Ruby behavior and Java channels. JRuby 9.2.13.0, released on August 3, repairs defects at both boundaries. The release updates the YAML stack for an alias-expansion vulnerability and ensures that stream copying writes every byte it has read.
The release remains in the 9.2 series, which targets Ruby 2.5 compatibility. Its maintenance scope includes internal concurrency fixes as well as library updates. The common concern is preserving Ruby-level behavior when the implementation passes work through another layer.
I find Ruby's interfaces approachable, but compatibility needs more than matching method names. A call that returns after copying a stream must preserve the bytes, and a library exposed as YAML must account for the parser it embeds. These implementation boundaries are relevant even when application code never mentions Java.
Psych brings SnakeYAML into the runtime
Issue 6320 reports a vulnerability scan finding SnakeYAML 1.23 inside the JRuby distribution. The report identifies the embedded Java archive and asks for version 1.26 or later to address CVE-2017-18640. The release announcement describes the issue as an alias-expansion bug.
The Psych update changes its default SnakeYAML version from 1.23 to 1.26 when the Ruby engine is JRuby. JRuby's dependency update then moves the bundled Psych gem from 3.1.0 to 3.2.0. The artifact inclusion lists change with the dependency version.
Those are two related version changes with different roles. Psych defines the Java parser version used by that implementation. JRuby chooses which Psych distribution it bundles. Updating a declaration in one repository would not be a complete account of the delivered runtime without the integration change in the other.
For a user assessing the runtime artifact, this explains why a dependency scanner can identify a Java library inside a Ruby distribution. The loaded language-level API does not erase the dependencies implementing it. The release is relevant to that embedded parser even if the application never lists SnakeYAML directly.
Alias handling also belongs below many application-level YAML calls. An application can ask for one document while the parser handles relationships inside it. The reported security correction concerns that parser behavior. The primary sources establish the affected dependency and replacement version, without requiring a speculative claim about which application documents have triggered it.
A read count is not a write count
The IO.copy_stream patch is small enough to explain directly. The transfer loop reads bytes into a buffer, flips the buffer for output, writes it to the destination and then clears it for the next read. Before the fix, it called the destination's write method only once.
A single write can consume fewer bytes than the buffer contains. Clearing the buffer immediately afterward discards the remaining bytes from the copy operation. The surrounding loop can continue reading, but the output no longer contains everything already consumed from the input.
The patch introduces a written-byte count and repeats writes until that count reaches the number read. Only then does it clear the buffer. The transfer's accounting can consequently advance after the whole chunk has been delivered to the destination.
This is a distinction between one operation and the higher-level promise of copying. The underlying channel can legitimately perform a partial write. IO.copy_stream must compose enough operations to satisfy its own contract. Treating the lower-level call as though it always drains the buffer incorrectly transfers responsibility to the channel.
The release announcement connects the defect to hangs in net/http and other libraries. Missing bytes can leave a receiver waiting for data that the sender's copy loop believes it has already transferred. That makes a timeout look like a network symptom even when the underlying cause is local buffer handling.
The buffer has a lifecycle within each transfer
The ordering of buffer operations is essential to the correction. Flipping prepares the bytes just read for writing. Clearing prepares the storage for reuse. The patch places the complete write loop between those two transitions, so storage is not reused while some of its current content still needs transmission.
A count accumulated from reads cannot by itself establish successful delivery. The implementation needs to preserve the relationship between bytes consumed and bytes written before advancing to the next chunk. This principle follows directly from the changed loop, without depending on the size of any particular network transfer.
I prefer that concrete explanation to describing the release as improving I/O reliability. The exact defect is losing the remainder of a partial write. Repeating writes before clearing the buffer repairs the relationship between the copy method's input and output.
Optimization data must be published consistently
The release also identifies internal optimization structures that other threads could observe before an update was complete. These are runtime implementation structures, so a failure can appear in JRuby's core even when the application has not deliberately changed shared optimization state.
The linked runtime restructuring moves several pieces of intermediate-representation state into interpreter contexts and changes the corresponding access paths. This is broader work than the stream-copy loop. The release describes its purpose as making runtime-sensitive optimization state safer under concurrent use.
That matters to a compatibility runtime because executing the same Ruby code on multiple threads can exercise internal compilation and interpretation machinery. The implementation has to maintain its own state consistently while preserving the program's language-level behavior. Application locking cannot substitute for correctness of those private runtime structures.
JRuby 9.2.13.0 also lists fixes in FFI memory ownership and Windows socket-error translation. The YAML integration and stream-copy changes give particularly direct examples of why a runtime maintenance update can matter without adding a new Ruby syntax feature. Both repair behavior beneath existing application calls, where the implementation is responsible for satisfying the interface it already exposes.