Project

General

Profile

Actions

Bug #15792

closed

GC can leave strings used as hash keys in a corrupted state

Added by byroot (Jean Boussier) over 5 years ago. Updated about 5 years ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:92412]

Description

The following script showcase the issue:

#!/usr/bin/env ruby --disable-gems
a = ('a' * 24).encode(Encoding::ASCII).gsub('x', '')
b = ('b' * 24).encode(Encoding::ASCII).gsub('x', '')

hash = {}
hash[a] = true
hash[b] = true

puts "Bebore garbage_collection: a=#{a.inspect} b=#{b.inspect}"
4.times { GC.start }
puts "After garbage_collection: a=#{a.inspect} b=#{b.inspect}"

Expected output:

Bebore garbage_collection: a="aaaaaaaaaaaaaaaaaaaaaaaa" b="bbbbbbbbbbbbbbbbbbbbbbbb"
After garbage_collection: a="aaaaaaaaaaaaaaaaaaaaaaaa" b="bbbbbbbbbbbbbbbbbbbbbbbb"

Actual output:

Ruby: 2.6.2
Bebore garbage_collection: a="aaaaaaaaaaaaaaaaaaaaaaaa" b="bbbbbbbbbbbbbbbbbbbbbbbb"
After garbage_collection: a="}\x0Eu\xDB\xFC\a\x00\x80\xE9\ru\xDB\xFC\a\x00\x10\x04\x00aaaaaa" b="\x00\x00\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x00\xC0\x02\x00bbbbbb"

We reduced the repro script as much as we could, both the .encode(ASCII) and the gsub are necessary for the bug to manifest itself.

We also used ObjectSpace.dump() to analyze the corrupted string.

b = "shared":true, "encoding":"US-ASCII", "references":["0x7faf4a01aeb8"]
0x7faf4a01aeb8 = "frozen":true, "fstring":true, "bytesize":24, "value":"bbbbbbbbbbbbbbbbbbbbbbbb", "encoding":"US-ASCII"

Big thanks to Édouard Chin who did most of the initial repro reduction.

Updated by byroot (Jean Boussier) over 5 years ago

Actually, even simpler repro script:

#!/usr/bin/env ruby --disable-gems
a = ('a' * 24).encode(Encoding::ASCII).strip

hash = {}
hash[a] = true

puts "Before garbage_collection: a=#{a.inspect}"
4.times { GC.start }
puts "After garbage_collection: a=#{a.inspect}"

Updated by byroot (Jean Boussier) over 5 years ago

Édouad Chin bisected the script against the MRI repository, and found https://bugs.ruby-lang.org/issues/15251 the first commit to trigger the problem.

That being said it's unlikely to really be the source of the bug. It's much more likely to be in the fstring function.

Updated by wanabe (_ wanabe) over 5 years ago

I guess it is caused by the combination of str_duplicate and rb_fstring from r52074.

$ cat bug15792.rb 
a = ('a' * 24).b.strip

->{ eval "", binding, a, 1 }.call

puts "Before garbage_collection: a=#{a.inspect}"
4.times { GC.start }
puts "After garbage_collection: a=#{a.inspect}"

$ ./miniruby -v bug15792.rb 
ruby 2.3.0dev (2015-10-07 trunk 52074) [x86_64-linux]
Before garbage_collection: a="aaaaaaaaaaaaaaaaaaaaaaaa"
After garbage_collection: a="\x00B\xFB\xCCmU\x00\x00\x10\x10\xE7\xCCmU\x00\x00aaaaaaaa"
Actions #5

Updated by nagachika (Tomoyuki Chikanaga) over 5 years ago

  • Backport changed from 2.4: UNKNOWN, 2.5: UNKNOWN, 2.6: UNKNOWN to 2.4: UNKNOWN, 2.5: UNKNOWN, 2.6: REQUIRED
Actions #6

Updated by nobu (Nobuyoshi Nakada) over 5 years ago

  • Backport changed from 2.4: UNKNOWN, 2.5: UNKNOWN, 2.6: REQUIRED to 2.4: REQUIRED, 2.5: REQUIRED, 2.6: REQUIRED

Updated by nagachika (Tomoyuki Chikanaga) about 5 years ago

  • Backport changed from 2.4: REQUIRED, 2.5: REQUIRED, 2.6: REQUIRED to 2.4: REQUIRED, 2.5: REQUIRED, 2.6: DONE

ruby_2_6 r67731 merged revision(s) 3f9562015e651735bfc2fdd14e8f6963b673e22a,c06ddfee878524168e4af07443217ed2f8d0954b,3b3b4a44e57dfe03ce3913009d69a33d6f6100be.

Updated by usa (Usaku NAKAMURA) about 5 years ago

  • Backport changed from 2.4: REQUIRED, 2.5: REQUIRED, 2.6: DONE to 2.4: REQUIRED, 2.5: DONE, 2.6: DONE

ruby_2_5 r67766 merged revision(s) 3f9562015e651735bfc2fdd14e8f6963b673e22a,c06ddfee878524168e4af07443217ed2f8d0954b,3b3b4a44e5.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0