mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
[ruby/psych] Clamp io_reader copy to libyaml's buffer size
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>
This commit is contained in:
1 parent
cabe750b80
commit
770ccaa6fb
2 files changed
+32
-2
No files matched your search
@@ -33,8 +33,15 @@ static int io_reader(void * data, unsigned char *buf, size_t size, size_t *read)
|
||||
|
||||
if(! NIL_P(string)) {
|
||||
void * str = (void *)StringValuePtr(string);
|
||||
*read = (size_t)RSTRING_LEN(string);
|
||||
memcpy(buf, str, *read);
|
||||
size_t len = (size_t)RSTRING_LEN(string);
|
||||
|
||||
/* IO#read(size) is documented to return at most `size` bytes, but a
|
||||
* misbehaving IO-like object may return more. Clamp the copy to the
|
||||
* buffer libyaml gave us to avoid writing past its end. */
|
||||
if(len > size) len = size;
|
||||
|
||||
*read = len;
|
||||
memcpy(buf, str, len);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -198,6 +198,29 @@ module Psych
|
||||
assert_called :end_stream
|
||||
end
|
||||
|
||||
def test_parse_io_returns_more_bytes_than_requested
|
||||
# An IO-like source whose #read returns more bytes than the size it was
|
||||
# asked for must not overflow libyaml's read buffer.
|
||||
io = Object.new
|
||||
def io.external_encoding; Encoding::UTF_8 end
|
||||
def io.read len
|
||||
return nil if @done
|
||||
@done = true
|
||||
"--- a\n" + ("#" * (len + (1 << 20)))
|
||||
end
|
||||
|
||||
# CRuby clamps the over-read and parses; JRuby's parser rejects the
|
||||
# over-reading IO with an IOError. Either way there is no overflow.
|
||||
begin
|
||||
@parser.parse io
|
||||
rescue IOError
|
||||
return
|
||||
end
|
||||
assert_called :start_stream
|
||||
assert_called :scalar
|
||||
assert_called :end_stream
|
||||
end
|
||||
|
||||
def test_syntax_error
|
||||
assert_raise(Psych::SyntaxError) do
|
||||
@parser.parse("---\n\"foo\"\n\"bar\"\n")
|
||||
|
||||
Reference in new issue
Block a user