ruby/test/-ext-/string/test_fstring.rb
nagachika 641e384341 merge revision(s) 93faa011d393bb4b5cf31a0cbb46922f0a5e7cdc: [Backport #16151]
Tag string shared roots to fix use-after-free

	The buffer deduplication codepath in rb_fstring can be used to free the buffer
	of shared string roots, which leads to use-after-free.

	Introudce a new flag to tag strings that at one point have been a shared root.
	Check for it in rb_fstring to avoid freeing buffers that are shared by
	multiple strings. This change is based on nobu's idea in [ruby-core:94838].

	The included test case test for the sequence of calls to internal functions
	that lead to this bug. See attached ticket for Ruby level repros.

	[Bug #16151]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_6@67804 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2019-09-27 11:23:18 +00:00

84 lines
2.1 KiB
Ruby

# frozen_string_literal: false
require 'test/unit'
require '-test-/string'
require_relative '../symbol/noninterned_name'
class Test_String_Fstring < Test::Unit::TestCase
include Test_Symbol::NonInterned
def assert_fstring(str)
fstr = Bug::String.fstring(str)
yield str
yield fstr
end
def test_taint_shared_string
str = __method__.to_s.dup
str.taint
assert_fstring(str) {|s| assert_predicate(s, :tainted?)}
end
def test_taint_normal_string
str = __method__.to_s * 3
str.taint
assert_fstring(str) {|s| assert_predicate(s, :tainted?)}
end
def test_taint_registered_tainted
str = __method__.to_s * 3
str.taint
assert_fstring(str) {|s| assert_predicate(s, :tainted?)}
str = __method__.to_s * 3
assert_fstring(str) {|s| assert_not_predicate(s, :tainted?)}
end
def test_taint_registered_untainted
str = __method__.to_s * 3
assert_fstring(str) {|s| assert_not_predicate(s, :tainted?)}
str = __method__.to_s * 3
str.taint
assert_fstring(str) {|s| assert_predicate(s, :tainted?)}
end
def test_instance_variable
str = __method__.to_s * 3
str.instance_variable_set(:@test, 42)
str.freeze
assert_fstring(str) {|s| assert_send([s, :instance_variable_defined?, :@test])}
end
def test_singleton_method
str = __method__.to_s * 3
def str.foo
end
str.freeze
assert_fstring(str) {|s| assert_send([s, :respond_to?, :foo])}
end
def test_singleton_class
str = noninterned_name.force_encoding("us-ascii")
fstr = Bug::String.fstring(str)
assert_raise(TypeError) {fstr.singleton_class}
end
class S < String
end
def test_subclass
str = S.new(__method__.to_s * 3)
str.freeze
assert_fstring(str) {|s| assert_instance_of(S, s)}
end
def test_shared_string_safety
-('a' * 30).force_encoding(Encoding::ASCII)
str = ('a' * 30).force_encoding(Encoding::ASCII).taint
frozen_str = Bug::String.rb_str_new_frozen(str)
assert_fstring(frozen_str) {|s| assert_equal(str, s)}
GC.start
assert_equal('a' * 30, str, "[Bug #16151]")
end
end