1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/ruby/test_marshal.rb
Jeremy Evans ffd0820ab3 Deprecate taint/trust and related methods, and make the methods no-ops
This removes the related tests, and puts the related specs behind
version guards.  This affects all code in lib, including some
libraries that may want to support older versions of Ruby.
2019-11-18 01:00:25 +02:00

756 lines
19 KiB
Ruby

# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
require_relative 'marshaltestlib'
class TestMarshal < Test::Unit::TestCase
include MarshalTestLib
def setup
@verbose = $VERBOSE
$VERBOSE = nil
end
def teardown
$VERBOSE = @verbose
end
def encode(o)
Marshal.dump(o)
end
def decode(s)
Marshal.load(s)
end
def fact(n)
return 1 if n == 0
f = 1
while n>0
f *= n
n -= 1
end
return f
end
def test_marshal
a = [1, 2, 3, [4,5,"foo"], {1=>"bar"}, 2.5, fact(30)]
assert_equal a, Marshal.load(Marshal.dump(a))
[[1,2,3,4], [81, 2, 118, 3146]].each { |w,x,y,z|
obj = (x.to_f + y.to_f / z.to_f) * Math.exp(w.to_f / (x.to_f + y.to_f / z.to_f))
assert_equal obj, Marshal.load(Marshal.dump(obj))
}
bug3659 = '[ruby-dev:41936]'
[1.0, 10.0, 100.0, 110.0].each {|x|
assert_equal(x, Marshal.load(Marshal.dump(x)), bug3659)
}
end
StrClone = String.clone
def test_marshal_cloned_class
assert_instance_of(StrClone, Marshal.load(Marshal.dump(StrClone.new("abc"))))
end
def test_inconsistent_struct
TestMarshal.const_set :StructOrNot, Struct.new(:a)
s = Marshal.dump(StructOrNot.new(1))
TestMarshal.instance_eval { remove_const :StructOrNot }
TestMarshal.const_set :StructOrNot, Class.new
assert_raise(TypeError, "[ruby-dev:31709]") { Marshal.load(s) }
end
def test_struct_invalid_members
TestMarshal.const_set :StructInvalidMembers, Struct.new(:a)
assert_raise(TypeError, "[ruby-dev:31759]") {
Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo")
TestMarshal::StructInvalidMembers.members
}
end
class C
def initialize(str)
@str = str
end
attr_reader :str
def _dump(limit)
@str
end
def self._load(s)
new(s)
end
end
def test_too_long_string
data = Marshal.dump(C.new("a".force_encoding("ascii-8bit")))
data[-2, 1] = "\003\377\377\377"
assert_raise_with_message(ArgumentError, "marshal data too short", "[ruby-dev:32054]") {
Marshal.load(data)
}
end
def test_userdef_encoding
s1 = "\xa4\xa4".force_encoding("euc-jp")
o1 = C.new(s1)
m = Marshal.dump(o1)
o2 = Marshal.load(m)
s2 = o2.str
assert_equal(s1, s2)
end
def test_pipe
o1 = C.new("a" * 10000)
IO.pipe do |r, w|
th = Thread.new {Marshal.dump(o1, w)}
o2 = Marshal.load(r)
th.join
assert_equal(o1.str, o2.str)
end
IO.pipe do |r, w|
th = Thread.new {Marshal.dump(o1, w, 2)}
o2 = Marshal.load(r)
th.join
assert_equal(o1.str, o2.str)
end
assert_raise(TypeError) { Marshal.dump("foo", Object.new) }
assert_raise(TypeError) { Marshal.load(Object.new) }
end
def test_limit
assert_equal([[[]]], Marshal.load(Marshal.dump([[[]]], 3)))
assert_raise(ArgumentError) { Marshal.dump([[[]]], 2) }
assert_nothing_raised(ArgumentError, '[ruby-core:24100]') { Marshal.dump("\u3042", 1) }
end
def test_userdef_invalid
o = C.new(nil)
assert_raise(TypeError) { Marshal.dump(o) }
end
def test_class
o = class << Object.new; self; end
assert_raise(TypeError) { Marshal.dump(o) }
assert_equal(Object, Marshal.load(Marshal.dump(Object)))
assert_equal(Enumerable, Marshal.load(Marshal.dump(Enumerable)))
end
class C2
def initialize(ary)
@ary = ary
end
def _dump(s)
@ary.clear
"foo"
end
end
def test_modify_array_during_dump
a = []
o = C2.new(a)
a << o << nil
assert_raise(RuntimeError) { Marshal.dump(a) }
end
def test_change_class_name
eval("class C3; def _dump(s); 'foo'; end; end")
m = Marshal.dump(C3.new)
assert_raise(TypeError) { Marshal.load(m) }
eval("C3 = nil")
assert_raise(TypeError) { Marshal.load(m) }
end
def test_change_struct
eval("C3 = Struct.new(:foo, :bar)")
m = Marshal.dump(C3.new("FOO", "BAR"))
eval("C3 = Struct.new(:foo)")
assert_raise(TypeError) { Marshal.load(m) }
eval("C3 = Struct.new(:foo, :baz)")
assert_raise(TypeError) { Marshal.load(m) }
end
class C4
def initialize(gc)
@gc = gc
end
def _dump(s)
GC.start if @gc
"foo"
end
end
def test_gc
assert_nothing_raised do
Marshal.dump((0..1000).map {|x| C4.new(x % 50 == 25) })
end
end
def test_symbol2
[:ruby, :"\u{7d05}\u{7389}"].each do |sym|
assert_equal(sym, Marshal.load(Marshal.dump(sym)), '[ruby-core:24788]')
end
bug2548 = '[ruby-core:27375]'
ary = [:$1, nil]
assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug2548)
end
def test_symlink
assert_include(Marshal.dump([:a, :a]), ';')
end
def test_symlink_in_ivar
bug10991 = '[ruby-core:68587] [Bug #10991]'
sym = Marshal.load("\x04\x08" +
"I" ":\x0bKernel" +
("\x06" +
("I" ":\x07@a" +
("\x06" ":\x07@b" "e;\x0""o:\x0bObject""\x0")) +
"0"))
assert_equal(:Kernel, sym, bug10991)
end
ClassUTF8 = eval("class R\u{e9}sum\u{e9}; self; end")
iso_8859_1 = Encoding::ISO_8859_1
structISO8859_1 = Struct.new("r\xe9sum\xe9".force_encoding(iso_8859_1).intern)
const_set("R\xe9sum\xe9".force_encoding(iso_8859_1), structISO8859_1)
structISO8859_1.name
StructISO8859_1 = structISO8859_1
classISO8859_1 = Class.new do
attr_accessor "r\xe9sum\xe9".force_encoding(iso_8859_1)
eval("def initialize(x) @r\xe9sum\xe9 = x; end".force_encoding(iso_8859_1))
end
const_set("R\xe9sum\xe92".force_encoding(iso_8859_1), classISO8859_1)
classISO8859_1.name
ClassISO8859_1 = classISO8859_1
def test_class_nonascii
a = ClassUTF8.new
assert_instance_of(ClassUTF8, Marshal.load(Marshal.dump(a)), '[ruby-core:24790]')
bug1932 = '[ruby-core:24882]'
a = StructISO8859_1.new(10)
assert_nothing_raised(bug1932) do
assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932)
end
a.__send__("#{StructISO8859_1.members[0]}=", a)
assert_nothing_raised(bug1932) do
assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932)
end
a = ClassISO8859_1.new(10)
assert_nothing_raised(bug1932) do
b = Marshal.load(Marshal.dump(a))
assert_equal(ClassISO8859_1, b.class, bug1932)
assert_equal(a.instance_variables, b.instance_variables, bug1932)
a.instance_variables.each do |i|
assert_equal(a.instance_variable_get(i), b.instance_variable_get(i), bug1932)
end
end
a.__send__(a.methods(true).grep(/=\z/)[0], a)
assert_nothing_raised(bug1932) do
b = Marshal.load(Marshal.dump(a))
assert_equal(ClassISO8859_1, b.class, bug1932)
assert_equal(a.instance_variables, b.instance_variables, bug1932)
assert_equal(b, b.instance_variable_get(a.instance_variables[0]), bug1932)
end
end
def test_regexp2
assert_equal(/\\u/, Marshal.load("\004\b/\b\\\\u\000"))
assert_equal(/u/, Marshal.load("\004\b/\a\\u\000"))
assert_equal(/u/, Marshal.load("\004\bI/\a\\u\000\006:\016@encoding\"\vEUC-JP"))
bug2109 = '[ruby-core:25625]'
a = "\x82\xa0".force_encoding(Encoding::Windows_31J)
b = "\x82\xa2".force_encoding(Encoding::Windows_31J)
c = [/#{a}/, /#{b}/]
assert_equal(c, Marshal.load(Marshal.dump(c)), bug2109)
assert_nothing_raised(ArgumentError, '[ruby-dev:40386]') do
re = Tempfile.create("marshal_regexp") do |f|
f.binmode.write("\x04\bI/\x00\x00\x06:\rencoding\"\rUS-ASCII")
f.rewind
re2 = Marshal.load(f)
re2
end
assert_equal(//, re)
end
end
class DumpTest
def marshal_dump
@@block.call(:marshal_dump)
end
def dump_each(&block)
@@block = block
Marshal.dump(self)
end
end
class LoadTest
def marshal_dump
nil
end
def marshal_load(obj)
@@block.call(:marshal_load)
end
def self.load_each(m, &block)
@@block = block
Marshal.load(m)
end
end
def test_context_switch
o = DumpTest.new
e = o.enum_for(:dump_each)
assert_equal(:marshal_dump, e.next)
GC.start
assert(true, '[ruby-dev:39425]')
assert_raise(StopIteration) {e.next}
o = LoadTest.new
m = Marshal.dump(o)
e = LoadTest.enum_for(:load_each, m)
assert_equal(:marshal_load, e.next)
GC.start
assert(true, '[ruby-dev:39425]')
assert_raise(StopIteration) {e.next}
end
def test_dump_buffer
bug2390 = '[ruby-dev:39744]'
w = ""
def w.write(str)
self << str.to_s
end
Marshal.dump(Object.new, w)
assert_not_empty(w, bug2390)
end
class C5
def marshal_dump
"foo"
end
def marshal_load(foo)
@foo = foo
end
def initialize(x)
@x = x
end
end
def test_marshal_dump
c = C5.new("bar")
s = Marshal.dump(c)
d = Marshal.load(s)
assert_equal("foo", d.instance_variable_get(:@foo))
assert_equal(false, d.instance_variable_defined?(:@x))
end
class C6
def initialize
@stdin = STDIN
end
attr_reader :stdin
def marshal_dump
1
end
def marshal_load(x)
@stdin = STDIN
end
end
def test_marshal_dump_extra_iv
o = C6.new
m = nil
assert_nothing_raised("[ruby-dev:21475] [ruby-dev:39845]") {
m = Marshal.dump(o)
}
o2 = Marshal.load(m)
assert_equal(STDIN, o2.stdin)
end
def test_marshal_string_encoding
o1 = ["foo".force_encoding("EUC-JP")] + [ "bar" ] * 2
m = Marshal.dump(o1)
o2 = Marshal.load(m)
assert_equal(o1, o2, "[ruby-dev:40388]")
end
def test_marshal_regexp_encoding
o1 = [Regexp.new("r1".force_encoding("EUC-JP"))] + ["r2"] * 2
m = Marshal.dump(o1)
o2 = Marshal.load(m)
assert_equal(o1, o2, "[ruby-dev:40416]")
end
def test_marshal_encoding_encoding
o1 = [Encoding.find("EUC-JP")] + ["r2"] * 2
m = Marshal.dump(o1)
o2 = Marshal.load(m)
assert_equal(o1, o2)
end
def test_marshal_symbol_ascii8bit
bug6209 = '[ruby-core:43762]'
o1 = "\xff".force_encoding("ASCII-8BIT").intern
m = Marshal.dump(o1)
o2 = nil
assert_nothing_raised(EncodingError, bug6209) {o2 = Marshal.load(m)}
assert_equal(o1, o2, bug6209)
end
class PrivateClass
def initialize(foo)
@foo = foo
end
attr_reader :foo
end
private_constant :PrivateClass
def test_marshal_private_class
o1 = PrivateClass.new("test")
o2 = Marshal.load(Marshal.dump(o1))
assert_equal(o1.class, o2.class)
assert_equal(o1.foo, o2.foo)
end
def test_marshal_complex
assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x05")}
assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x06i\x00")}
assert_equal(Complex(1, 2), Marshal.load("\x04\bU:\fComplex[\ai\x06i\a"))
assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\bi\x00i\x00i\x00")}
end
def test_marshal_rational
assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x05")}
assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x06i\x00")}
assert_equal(Rational(1, 2), Marshal.load("\x04\bU:\rRational[\ai\x06i\a"))
assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\bi\x00i\x00i\x00")}
end
def test_marshal_flonum_reference
bug7348 = '[ruby-core:49323]'
e = []
ary = [ [2.0, e], [e] ]
assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug7348)
end
class TestClass
end
module TestModule
end
class Bug7627 < Struct.new(:bar)
attr_accessor :foo
def marshal_dump; 'dump'; end # fake dump data
def marshal_load(*); end # do nothing
end
def test_marshal_dump_struct_ivar
bug7627 = '[ruby-core:51163]'
obj = Bug7627.new
obj.foo = '[Bug #7627]'
dump = Marshal.dump(obj)
loaded = Marshal.load(dump)
assert_equal(obj, loaded, bug7627)
assert_nil(loaded.foo, bug7627)
end
class LoadData
attr_reader :data
def initialize(data)
@data = data
end
alias marshal_dump data
alias marshal_load initialize
end
class Bug8276 < LoadData
def initialize(*)
super
freeze
end
alias marshal_load initialize
end
class FrozenData < LoadData
def marshal_load(data)
super
data.instance_variables.each do |iv|
instance_variable_set(iv, data.instance_variable_get(iv))
end
freeze
end
end
def test_marshal_dump_excess_encoding
bug8276 = '[ruby-core:54334] [Bug #8276]'
t = Bug8276.new(bug8276)
s = Marshal.dump(t)
assert_nothing_raised(RuntimeError, bug8276) {s = Marshal.load(s)}
assert_equal(t.data, s.data, bug8276)
end
def test_marshal_dump_ivar
s = "data with ivar"
s.instance_variable_set(:@t, 42)
t = Bug8276.new(s)
s = Marshal.dump(t)
assert_raise(FrozenError) {Marshal.load(s)}
end
def test_marshal_load_ivar
s = "data with ivar"
s.instance_variable_set(:@t, 42)
hook = ->(v) {
if LoadData === v
assert_send([v, :instance_variable_defined?, :@t], v.class.name)
assert_equal(42, v.instance_variable_get(:@t), v.class.name)
end
v
}
[LoadData, FrozenData].each do |klass|
t = klass.new(s)
d = Marshal.dump(t)
v = assert_nothing_raised(RuntimeError) {break Marshal.load(d, hook)}
assert_send([v, :instance_variable_defined?, :@t], klass.name)
assert_equal(42, v.instance_variable_get(:@t), klass.name)
end
end
def test_class_ivar
assert_raise(TypeError) {Marshal.load("\x04\x08Ic\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
assert_not_operator(TestClass, :instance_variable_defined?, :@bug)
end
def test_module_ivar
assert_raise(TypeError) {Marshal.load("\x04\x08Im\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
assert_not_operator(TestModule, :instance_variable_defined?, :@bug)
end
class TestForRespondToFalse
def respond_to?(a)
false
end
end
def test_marshal_respond_to_arity
assert_nothing_raised(ArgumentError, '[Bug #7722]') do
Marshal.dump(TestForRespondToFalse.new)
end
end
def test_packed_string
packed = ["foo"].pack("p")
bare = "".force_encoding(Encoding::ASCII_8BIT) << packed
assert_equal(Marshal.dump(bare), Marshal.dump(packed))
end
class Bug9523
attr_reader :cc
def marshal_dump
callcc {|c| @cc = c }
nil
end
def marshal_load(v)
end
end
def test_continuation
require "continuation"
c = Bug9523.new
assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do
Marshal.dump(c)
GC.start
1000.times {"x"*1000}
GC.start
c.cc.call
end
end
def test_undumpable_message
c = Module.new {break module_eval("class IO\u{26a1} < IO;self;end")}
assert_raise_with_message(TypeError, /IO\u{26a1}/) {
Marshal.dump(c.new(0, autoclose: false))
}
end
def test_undumpable_data
c = Module.new {break module_eval("class T\u{23F0 23F3}<Time;undef _dump;self;end")}
assert_raise_with_message(TypeError, /T\u{23F0 23F3}/) {
Marshal.dump(c.new)
}
end
def test_unloadable_data
c = eval("class Unloadable\u{23F0 23F3}<Time;;self;end")
c.class_eval {
alias _dump_data _dump
undef _dump
}
d = Marshal.dump(c.new)
assert_raise_with_message(TypeError, /Unloadable\u{23F0 23F3}/) {
Marshal.load(d)
}
end
def test_unloadable_userdef
c = eval("class Userdef\u{23F0 23F3}<Time;self;end")
class << c
undef _load
end
d = Marshal.dump(c.new)
assert_raise_with_message(TypeError, /Userdef\u{23F0 23F3}/) {
Marshal.load(d)
}
end
def test_unloadable_usrmarshal
c = eval("class UsrMarshal\u{23F0 23F3}<Time;self;end")
c.class_eval {
alias marshal_dump _dump
}
d = Marshal.dump(c.new)
assert_raise_with_message(TypeError, /UsrMarshal\u{23F0 23F3}/) {
Marshal.load(d)
}
end
def test_no_internal_ids
opt = %w[--disable=gems]
args = [opt, 'Marshal.dump("",STDOUT)', true, true, encoding: Encoding::ASCII_8BIT]
out, err, status = EnvUtil.invoke_ruby(*args)
assert_empty(err)
assert_predicate(status, :success?)
expected = out
opt << "--enable=frozen-string-literal"
opt << "--debug=frozen-string-literal"
out, err, status = EnvUtil.invoke_ruby(*args)
assert_empty(err)
assert_predicate(status, :success?)
assert_equal(expected, out)
end
def test_marshal_honor_post_proc_value_for_link
str = 'x' # for link
obj = [str, str]
assert_equal(['X', 'X'], Marshal.load(Marshal.dump(obj), ->(v) { v == str ? v.upcase : v }))
end
def test_marshal_load_extended_class_crash
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
assert_raise_with_message(ArgumentError, /undefined/) do
Marshal.load("\x04\be:\x0F\x00omparableo:\vObject\x00")
end
end;
end
def test_marshal_load_r_prepare_reference_crash
crash = "\x04\bI/\x05\x00\x06:\x06E{\x06@\x05T"
opt = %w[--disable=gems]
assert_separately(opt, <<-RUBY)
assert_raise_with_message(ArgumentError, /bad link/) do
Marshal.load(#{crash.dump})
end
RUBY
end
MethodMissingWithoutRespondTo = Struct.new(:wrapped_object) do
undef respond_to?
def method_missing(*args, &block)
wrapped_object.public_send(*args, &block)
end
def respond_to_missing?(name, private = false)
wrapped_object.respond_to?(name, false)
end
end
def test_method_missing_without_respond_to
bug12353 = "[ruby-core:75377] [Bug #12353]: try method_missing if" \
" respond_to? is undefined"
obj = MethodMissingWithoutRespondTo.new("foo")
dump = assert_nothing_raised(NoMethodError, bug12353) do
Marshal.dump(obj)
end
assert_equal(obj, Marshal.load(dump))
end
class Bug12974
def marshal_dump
dup
end
end
def test_marshal_dump_recursion
assert_raise_with_message(RuntimeError, /same class instance/) do
Marshal.dump(Bug12974.new)
end
end
Bug14314 = Struct.new(:foo, keyword_init: true)
def test_marshal_keyword_init_struct
obj = Bug14314.new(foo: 42)
assert_equal obj, Marshal.load(Marshal.dump(obj))
end
class Bug15968
attr_accessor :bar, :baz
def initialize
self.bar = Bar.new(self)
end
class Bar
attr_accessor :foo
def initialize(foo)
self.foo = foo
end
def marshal_dump
if self.foo.baz
self.foo.remove_instance_variable(:@baz)
else
self.foo.baz = :problem
end
{foo: self.foo}
end
def marshal_load(data)
self.foo = data[:foo]
end
end
end
def test_marshal_dump_adding_instance_variable
obj = Bug15968.new
assert_raise_with_message(RuntimeError, /instance variable added/) do
Marshal.dump(obj)
end
end
def test_marshal_dump_removing_instance_variable
obj = Bug15968.new
obj.baz = :Bug15968
assert_raise_with_message(RuntimeError, /instance variable removed/) do
Marshal.dump(obj)
end
end
end