ruby-changes:65970
From: Yusuke <ko1@a...>
Date: Mon, 26 Apr 2021 22:47:12 +0900 (JST)
Subject: [ruby-changes:65970] 2c7d3b3a72 (master): node.c (rb_ast_new): imemo_ast is WB-unprotected
https://git.ruby-lang.org/ruby.git/commit/?id=2c7d3b3a72 From 2c7d3b3a722c4636ab1e9d289cbca47ddd168d3e Mon Sep 17 00:00:00 2001 From: Yusuke Endoh <mame@r...> Date: Mon, 26 Apr 2021 18:11:46 +0900 Subject: node.c (rb_ast_new): imemo_ast is WB-unprotected Previously imemo_ast was handled as WB-protected which caused a segfault of the following code: # shareable_constant_value: literal M0 = {} M1 = {} ... M100000 = {} My analysis is here: `shareable_constant_value: literal` creates many Hash instances during parsing, and add them to node_buffer of imemo_ast. However, the contents are missed because imemo_ast is incorrectly WB-protected. This changeset makes imemo_ast as WB-unprotected. --- gc.c | 7 +++++++ internal/imemo.h | 1 + node.c | 2 +- test/ruby/test_gc.rb | 7 +++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/gc.c b/gc.c index 4d78b85..c7e9754 100644 --- a/gc.c +++ b/gc.c @@ -2435,6 +2435,13 @@ rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0) https://github.com/ruby/ruby/blob/trunk/gc.c#L2435 return newobj_of(v0, flags, v1, v2, v3, TRUE); } +rb_ast_t * +rb_imemo_ast_new(VALUE node_buffer) +{ + VALUE flags = T_IMEMO | (imemo_ast << FL_USHIFT); + return (rb_ast_t *)newobj_of(node_buffer, flags, 0, 0, 0, FALSE); +} + static VALUE rb_imemo_tmpbuf_new(VALUE v1, VALUE v2, VALUE v3, VALUE v0) { diff --git a/internal/imemo.h b/internal/imemo.h index a9e2136..102356c 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -130,6 +130,7 @@ struct MEMO { https://github.com/ruby/ruby/blob/trunk/internal/imemo.h#L130 typedef struct rb_imemo_tmpbuf_struct rb_imemo_tmpbuf_t; VALUE rb_imemo_new(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0); +struct rb_ast_struct *rb_imemo_ast_new(VALUE node_buffer); rb_imemo_tmpbuf_t *rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt); struct vm_ifunc *rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int max_argc); void rb_strterm_mark(VALUE obj); diff --git a/node.c b/node.c index bef9d7b..2392d5b 100644 --- a/node.c +++ b/node.c @@ -1299,7 +1299,7 @@ rb_ast_t * https://github.com/ruby/ruby/blob/trunk/node.c#L1299 rb_ast_new(void) { node_buffer_t *nb = rb_node_buffer_new(); - rb_ast_t *ast = (rb_ast_t *)rb_imemo_new(imemo_ast, 0, 0, 0, (VALUE)nb); + rb_ast_t *ast = rb_imemo_ast_new((VALUE)nb); return ast; } diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index 1f75a34..f10946b 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -494,4 +494,11 @@ class TestGc < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_gc.rb#L494 b = 1000.times.map { Object.new.object_id } assert_empty(a & b) end + + def test_ast_node_buffer + # https://github.com/ruby/ruby/pull/4416 + Module.new.class_eval do + eval((["# shareable_constant_value: literal"] + (0..100000).map {|i| "M#{ i } = {}" }).join("\n")) + end + end end -- cgit v1.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/