diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-12-20 06:44:50 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-12-20 06:44:50 +0000 |
commit | ec7b1e475b1f8cb3ca775fec27227c6761d3b203 (patch) | |
tree | e9076717079404af996b46dce24cded888dfc0b6 | |
parent | f89238ec0d689f3989c55c4716da500b8c898900 (diff) |
Freeze hash literals embedded in duphash instructions
Previously, these hash literals were not frozen, and thus could be
modified by ObjectSpace, resulting in undesired behavior. Example:
```ruby
require 'objspace'
def a(b={0=>1,1=>4,2=>17})
b
end
p a
ObjectSpace.each_object(Hash) do |a|
a[3] = 8 if a.class == Hash && a[0] == 1 && a[1] == 4 && a[2] == 17
end
p a
```
It may be desirable to hide such hashes from ObjectSpace, since
they are internal, but I'm not sure how to do that.
From: Jeremy Evans <[email protected]>
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66464 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | compile.c | 1 | ||||
-rw-r--r-- | test/ruby/test_literal.rb | 16 |
2 files changed, 17 insertions, 0 deletions
@@ -4026,6 +4026,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_ro hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2); rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR_TRANSIENT(ary), hash); + rb_hash_freeze(hash); iseq_add_mark_object_compile_time(iseq, hash); ADD_INSN1(ret, line, duphash, hash); } diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb index ef2467e177..37755422a1 100644 --- a/test/ruby/test_literal.rb +++ b/test/ruby/test_literal.rb @@ -283,6 +283,22 @@ class TestRubyLiteral < Test::Unit::TestCase assert_equal "literal", h["string"] end + def frozen_hash_literal_arg + {0=>1,1=>4,2=>17} + end + + def test_hash_literal_frozen + assert_not_include frozen_hash_literal_arg, 3 + assert_raise(FrozenError) do + ObjectSpace.each_object(Hash) do |a| + if a.class == Hash and !a.default_proc and a.size == 3 + a[3] = 8 if a[0] == 1 and a[1] == 4 and a[2] == 17 + end + end + end + assert_not_include frozen_hash_literal_arg, 3 + end + def test_big_array_and_hash_literal assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).map{'x'}.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems] assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).to_a.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems] |