The parser calls back into Ruby for every event, so a handler can call
#parse again on the same object. That reinitialised the parser and
re-pointed it at new input while the outer loop was still driving it,
which left the outer loop reading the inner document's freed buffer, or
resuming on a parser with no input at all and tripping libyaml's
read_handler assertion.
Psych::Exception is used so the caller can rescue it, and the in-use
flag is cleared with rb_ensure so a parser stays reusable afterwards.
https://github.com/ruby/psych/commit/5f2e37dc1c
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every case of the event switch overwrote the rb_protect state, so only
the second result was ever tested and the state from the event_location
call was dropped. The parse then returned normally with the exception
silently discarded.
https://github.com/ruby/psych/commit/186c3fa343
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reject canonical output with NotImplementedError instead of silently
ignoring the request, and honor the implicit flag in start_sequence and
start_mapping so an implicit tag is not printed as a redundant verbose
tag. Relax the unquoted-boolean dump test to allow an optional document
end marker. Also correct the version docstrings and a stale comment
about how the parser collects diagnostics.
https://github.com/ruby/psych/commit/23f60daff1
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover what the libfyaml backend does distinctly, each placed with the
concern it belongs to: the Psych::BACKEND and Psych.libfyaml_version
checks in test_psych.rb, the YAML 1.2 "Norway problem" boolean case in
test_boolean.rb, and the "1.1 booleans are not quoted" emission case in
test_string.rb. The 1.2 assertions are skipped on the default libyaml
backend so the same suite passes under both.
https://github.com/ruby/psych/commit/35d0a437a2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a libfyaml? helper and guard the tests that intentionally diverge on
the experimental YAML 1.2 backend: yes/no/on/off are strings rather than
booleans, flow collections and block scalars are formatted differently,
tags and marks are reported differently, and non-ASCII tags/aliases are
rejected. test_boolean asserts the 1.2 string result directly; the
formatting and mark cases are skipped. The default libyaml build is
unaffected (every guard keys off Psych::BACKEND).
https://github.com/ruby/psych/commit/67b6365f5a
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
libyaml only discounts the BOM when it detects the stream encoding by
itself. Psych passes the encoding explicitly whenever it is known, and
on that path libyaml counts the BOM as a first-line character, shifting
every token on the first line one column right and silently terminating
a block mapping at the second line.
https://github.com/ruby/psych/issues/331https://github.com/ruby/psych/commit/924c15ed1f
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
!ruby/array, !seq and !ruby/string carry the same exposure just fixed
for the hash tags: the loader allocated the named class and replaced its
contents without checking the class was actually an Array or String
subclass. Apply the same subclass check so a permitted unrelated class
can no longer be allocated and populated through these tags.
https://github.com/ruby/psych/commit/73f0131b13
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
!ruby/hash-with-ivars, !ruby/hash and !map are only emitted for Hash
subclasses, but the loader allocated whatever class the tag named and
populated its ivars directly. That let a permitted non-Hash class be
instantiated with attacker-chosen ivars, bypassing its init_with
validation. Verify the resolved class is a Hash subclass before use.
https://github.com/ruby/psych/commit/b08a71012e
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
safe_load resolved !ruby/encoding directly via ::Encoding.find,
bypassing the permitted_classes check that !ruby/object:Encoding
already honors. Load it through the class loader so Encoding is only
deserialized when permitted.
https://github.com/ruby/psych/commit/f2e4b9d365
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clamping the over-read at exactly the requested size could split a
multibyte character, since the string an IO returns may carry a
non-binary encoding. Round the cut down to the last character boundary
at or before the size so the bytes handed to libyaml are always whole
characters.
https://github.com/ruby/psych/commit/6201ae17c1
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
io_reader trusted IO#read to honour its size argument and copied
RSTRING_LEN(string) bytes into libyaml's fixed-capacity buffer. An
IO-like object whose #read over-returns wrote past the buffer end, a
heap out-of-bounds write reachable from Psych.load/safe_load/parse.
Clamp the copied length to the requested size.
https://github.com/ruby/psych/commit/99ecd94560
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Added `Ractor::Port`
* `Ractor::Port#receive` (support multi-threads)
* `Rcator::Port#close`
* `Ractor::Port#closed?`
* Added some methods
* `Ractor#join`
* `Ractor#value`
* `Ractor#monitor`
* `Ractor#unmonitor`
* Removed some methods
* `Ractor#take`
* `Ractor.yield`
* Change the spec
* `Racotr.select`
You can wait for multiple sequences of messages with `Ractor::Port`.
```ruby
ports = 3.times.map{ Ractor::Port.new }
ports.map.with_index do |port, ri|
Ractor.new port,ri do |port, ri|
3.times{|i| port << "r#{ri}-#{i}"}
end
end
p ports.each{|port| pp 3.times.map{port.receive}}
```
In this example, we use 3 ports, and 3 Ractors send messages to them respectively.
We can receive a series of messages from each port.
You can use `Ractor#value` to get the last value of a Ractor's block:
```ruby
result = Ractor.new do
heavy_task()
end.value
```
You can wait for the termination of a Ractor with `Ractor#join` like this:
```ruby
Ractor.new do
some_task()
end.join
```
`#value` and `#join` are similar to `Thread#value` and `Thread#join`.
To implement `#join`, `Ractor#monitor` (and `Ractor#unmonitor`) is introduced.
This commit changes `Ractor.select()` method.
It now only accepts ports or Ractors, and returns when a port receives a message or a Ractor terminates.
We removes `Ractor.yield` and `Ractor#take` because:
* `Ractor::Port` supports most of similar use cases in a simpler manner.
* Removing them significantly simplifies the code.
We also change the internal thread scheduler code (thread_pthread.c):
* During barrier synchronization, we keep the `ractor_sched` lock to avoid deadlocks.
This lock is released by `rb_ractor_sched_barrier_end()`
which is called at the end of operations that require the barrier.
* fix potential deadlock issues by checking interrupts just before setting UBF.
https://bugs.ruby-lang.org/issues/21262
In Ruby < 3.0, the superclass of StringIO was actually already `Data`,
but it doesn't have the expected shape. So, on these earlier versions it errors:
> NoMethodError: undefined method `members' for #<StringIO:0x00005641dd5f2880>
> vendor/bundle/ruby/2.6.0/gems/psych-5.2.5/lib/psych/visitors/yaml_tree.rb:170:in `visit_Data'
This test doesn't fail on 2.7, presumably because it can pull in a newer `stringio` version.
https://github.com/ruby/psych/commit/0f40f56268
This sets the ivars _before_ calling initialize, which feels wrong. But
Data doesn't give us any mechanism for setting the members other than 1)
initialize, or 2) drop down into the C API. Since initialize freezes
the object, we need to set the ivars before that. I think this is a
reasonable compromise—if users need better handling, they can implement
their own `encode_with` and `init_with`. But it will lead to unhappy
surprises for some users.
Alternatively, we could use the C API, similarly to Marshal. Psych _is_
already using the C API for path2class and build_exception. This would
be the least surprising behavior for users, I think.
This fixes the issue where regular expression would come back slightly
different after going through a YAML load/dump cycle. Because we're used
to having to escape forward slashes in regular expression literals
(because the literal is delimited by slashes), but the deserializer
takes the literal output from `Regexp#inspect` and feeds it as a string
into `Regexp.new`, which expects a string, not a Regexp literal, cycling
did not properly work before this commit.
I've also changed the code to be a bit more readable, I hope this
doesn't affect performance.
https://github.com/ruby/psych/commit/f4dd8dadad
A string similar to "0x____" should be treated as a string.
Currently it is processed as an Integer.
This alters the regex specified by http://yaml.org/type/int.html
to ensure at least one numerical symbol is present in the string
before converting to Integer.
https://github.com/ruby/psych/commit/81479b203e
Previously, only certain values of the 3rd argument triggered a
deprecation warning.
First step for fix for bug #18797. Support for the 3rd argument
will be removed after the release of Ruby 3.2.
Fix minor fallout discovered by the tests.
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>