From 11bb12ad3cb34c2d1ef4f9b67b7779aea5e7e5f8 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 19 Sep 2026 20:02:28 -0500 Subject: [PATCH 1/3] [DOC] Doc for File.new - #18928 --- io.c | 60 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/io.c b/io.c index 4ce0aaf2026c81..0fcbe4875dcd7b 100644 --- a/io.c +++ b/io.c @@ -9744,43 +9744,51 @@ rb_io_set_encoding_by_bom(VALUE io) } /* + * :markup: markdown + * * call-seq: - * File.new(path, mode = 'r', perm = 0666, **opts) -> file + * File.new(path, mode = 'r', permissions = 0666, **options) -> file * - * Opens the file at the given +path+ according to the given +mode+; - * creates and returns a new File object for that file. + * Opens the file as specified by the given arguments. + * Creates and returns a new open \File object for that file; + * the opened file is in non-synchronous mode. * - * The new File object is buffered mode (or non-sync mode), unless - * +filename+ is a tty. - * See IO#flush, IO#fsync, IO#fdatasync, and IO#sync=. + * Argument `path` must the string path to an existing filesystem entry: * - * Argument +path+ must be a valid file path: + * ```ruby + * file = File.new('doc/maintainers.md') # => # + * file.close # Clean up. + * tty = File.new('/dev/tty') # => # + * tty.close # Clean up. + * ``` * - * f = File.new('/etc/fstab') - * f.close - * f = File.new('t.txt') - * f.close + * Note that the caller is responsible for closing the file; + * see File.open for automatic closing. * - * Optional argument +mode+ (defaults to 'r') must specify a valid mode; - * see {Access Modes}[rdoc-ref:File@Access+Modes]: + * Optional argument `mode` (defaults to `'r'`) must specify a valid mode; + * see [Access Modes](rdoc-ref:File@Access+Modes): * - * f = File.new('t.tmp', 'w') - * f.close - * f = File.new('t.tmp', File::RDONLY) - * f.close + * ```ruby + * file = File.new('t.tmp', 'w') # => # + * file.close # Clean up. + * file = File.new('t.tmp', File::RDONLY) # => # + * file.close # Clean up. + * ``` * - * Optional argument +perm+ (defaults to 0666) must specify valid permissions - * see {File Permissions}[rdoc-ref:File@File+Permissions]: + * Optional argument `permissions` (defaults to `0666`) must specify valid permissions; + * see [File Permissions](rdoc-ref:File@File+Permissions): * - * f = File.new('t.tmp', File::CREAT, 0644) - * f.close - * f = File.new('t.tmp', File::CREAT, 0444) - * f.close + * ```ruby + * file = File.new('t.tmp', 'w', 0644) # => # + * file.close # Clean up. + * file = File.new('t.tmp', 'w', 0444) # => # + * file.close # Clean up. + * ``` * - * Optional keyword arguments +opts+ specify: + * Optional keyword arguments `options` specify: * - * - {Open Options}[rdoc-ref:IO@Open+Options]. - * - {Encoding options}[rdoc-ref:encodings.rdoc@Encoding+Options]. + * - [Open Options](rdoc-ref:IO@Open+Options). + * - [Encoding options](rdoc-ref:encodings.rdoc@Encoding+Options). * */ From d1786641f1549c1f4954e4881de4bb4a19e9b213 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Sat, 19 Sep 2026 20:20:26 -0500 Subject: [PATCH 2/3] [DOC] Doc for File.open (#18930) --- io.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/io.c b/io.c index 0fcbe4875dcd7b..6ae91a6b37f07a 100644 --- a/io.c +++ b/io.c @@ -8266,33 +8266,47 @@ rb_open_file(VALUE io, VALUE fname, VALUE vmode, VALUE vperm, VALUE opt) /* * Document-method: File::open * + * :markup: markdown + * * call-seq: - * File.open(path, mode = 'r', perm = 0666, **opts) -> file - * File.open(path, mode = 'r', perm = 0666, **opts) {|f| ... } -> object + * File.open(path, mode = 'r', permissions = 0666, **options) -> file + * File.open(path, mode = 'r', permissions = 0666, **options) {|file| ... } -> object + * + * Creates a new \File object via File.new with the given arguments. * - * Creates a new File object, via File.new with the given arguments. + * With no block given, returns the \File object. * - * With no block given, returns the File object. + * With a block given, calls the block with the \File object, + * closes the \File object, and returns the block's value: * - * With a block given, calls the block with the File object - * and returns the block's value. + * ```ruby + * File.open('doc/maintainers.md') {|file| file.size } # => 14900 + * ``` * + * Note that the \File object is automatically closed + * even if the block raises an exception. */ /* * Document-method: IO::open * + * :markup: markdown + * * call-seq: - * IO.open(fd, mode = 'r', **opts) -> io - * IO.open(fd, mode = 'r', **opts) {|io| ... } -> object + * IO.open(fd, mode = 'r', **options) -> io + * IO.open(fd, mode = 'r', **options) {|io| ... } -> object * - * Creates a new \IO object, via IO.new with the given arguments. + * Creates a new \IO object via IO.new with the given arguments. * * With no block given, returns the \IO object. * - * With a block given, calls the block with the \IO object - * and returns the block's value. + * With a block given, calls the block with the \IO object, + * closes the \IO object, and returns the block’s value: * + * ```ruby + * fd = File.sysopen('doc/maintainers.md') # => 6 + * IO.open(fd) {|io| io.read.size } # => 14897 + * ``` */ static VALUE From 5a4ccd91b6d28712a9c4ca7f4290f89dc338ebc6 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sat, 19 Sep 2026 20:22:35 +0900 Subject: [PATCH 3/3] Fix use-after-free in String#each_line with separator modified The separator can be modified in the block yielded by String#each_line. When that happens, the pointer can change which can cause an use-after-free. For example, the following script crashes: sep = "x" * 1_000_000 s = "a#{sep}b#{sep}c" s.each_line(sep) { sep.clear } --- string.c | 1 + test/ruby/test_string.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/string.c b/string.c index 2129f97595f697..099933c5ddd859 100644 --- a/string.c +++ b/string.c @@ -10946,6 +10946,7 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, VALUE ary) line = rb_str_subseq(str, subptr - ptr, subend - subptr); if (ENUM_ELEM(ary, line)) { str_mod_check(str, ptr, len); + str_mod_check(rs, rsptr, rslen); } subptr = hit; } diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index db92a2cac1da83..0220a5f1e6803f 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -1619,6 +1619,16 @@ def test_each_line $VERBOSE = verbose end + def test_each_line_modified_separator + sep = S("x" * 1_000_000) + res = [] + assert_raise_with_message(RuntimeError, /string modified/) do + S("a#{sep}b#{sep}c").each_line(sep) do |x| + sep.clear + end + end + end + def test_each_line_chomp res = [] S("hello\nworld").each_line("\n", chomp: true) {|x| res << x}