1require 'test/unit' 2require 'tempfile' 3require_relative 'marshaltestlib' 4 5class TestMarshal < Test::Unit::TestCase 6 include MarshalTestLib 7 8 def setup 9 @verbose = $VERBOSE 10 $VERBOSE = nil 11 end 12 13 def teardown 14 $VERBOSE = @verbose 15 end 16 17 def encode(o) 18 Marshal.dump(o) 19 end 20 21 def decode(s) 22 Marshal.load(s) 23 end 24 25 def fact(n) 26 return 1 if n == 0 27 f = 1 28 while n>0 29 f *= n 30 n -= 1 31 end 32 return f 33 end 34 35 def test_marshal 36 a = [1, 2, 3, [4,5,"foo"], {1=>"bar"}, 2.5, fact(30)] 37 assert_equal a, Marshal.load(Marshal.dump(a)) 38 39 [[1,2,3,4], [81, 2, 118, 3146]].each { |w,x,y,z| 40 obj = (x.to_f + y.to_f / z.to_f) * Math.exp(w.to_f / (x.to_f + y.to_f / z.to_f)) 41 assert_equal obj, Marshal.load(Marshal.dump(obj)) 42 } 43 44 bug3659 = '[ruby-dev:41936]' 45 [1.0, 10.0, 100.0, 110.0].each {|x| 46 assert_equal(x, Marshal.load(Marshal.dump(x)), bug3659) 47 } 48 end 49 50 StrClone = String.clone 51 def test_marshal_cloned_class 52 assert_instance_of(StrClone, Marshal.load(Marshal.dump(StrClone.new("abc")))) 53 end 54 55 def test_inconsistent_struct 56 TestMarshal.const_set :StructOrNot, Struct.new(:a) 57 s = Marshal.dump(StructOrNot.new(1)) 58 TestMarshal.instance_eval { remove_const :StructOrNot } 59 TestMarshal.const_set :StructOrNot, Class.new 60 assert_raise(TypeError, "[ruby-dev:31709]") { Marshal.load(s) } 61 end 62 63 def test_struct_invalid_members 64 TestMarshal.const_set :StructInvalidMembers, Struct.new(:a) 65 assert_raise(TypeError, "[ruby-dev:31759]") { 66 Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo") 67 TestMarshal::StructInvalidMembers.members 68 } 69 end 70 71 class C 72 def initialize(str) 73 @str = str 74 end 75 attr_reader :str 76 def _dump(limit) 77 @str 78 end 79 def self._load(s) 80 new(s) 81 end 82 end 83 84 def test_too_long_string 85 data = Marshal.dump(C.new("a".force_encoding("ascii-8bit"))) 86 data[-2, 1] = "\003\377\377\377" 87 e = assert_raise(ArgumentError, "[ruby-dev:32054]") { 88 Marshal.load(data) 89 } 90 assert_equal("marshal data too short", e.message) 91 end 92 93 94 def test_userdef_encoding 95 s1 = "\xa4\xa4".force_encoding("euc-jp") 96 o1 = C.new(s1) 97 m = Marshal.dump(o1) 98 o2 = Marshal.load(m) 99 s2 = o2.str 100 assert_equal(s1, s2) 101 end 102 103 def test_pipe 104 o1 = C.new("a" * 10000) 105 106 o2 = IO.pipe do |r, w| 107 Thread.new {Marshal.dump(o1, w)} 108 Marshal.load(r) 109 end 110 assert_equal(o1.str, o2.str) 111 112 o2 = IO.pipe do |r, w| 113 Thread.new {Marshal.dump(o1, w, 2)} 114 Marshal.load(r) 115 end 116 assert_equal(o1.str, o2.str) 117 118 assert_raise(TypeError) { Marshal.dump("foo", Object.new) } 119 assert_raise(TypeError) { Marshal.load(Object.new) } 120 end 121 122 def test_limit 123 assert_equal([[[]]], Marshal.load(Marshal.dump([[[]]], 3))) 124 assert_raise(ArgumentError) { Marshal.dump([[[]]], 2) } 125 assert_nothing_raised(ArgumentError, '[ruby-core:24100]') { Marshal.dump("\u3042", 1) } 126 end 127 128 def test_userdef_invalid 129 o = C.new(nil) 130 assert_raise(TypeError) { Marshal.dump(o) } 131 end 132 133 def test_class 134 o = class << Object.new; self; end 135 assert_raise(TypeError) { Marshal.dump(o) } 136 assert_equal(Object, Marshal.load(Marshal.dump(Object))) 137 assert_equal(Enumerable, Marshal.load(Marshal.dump(Enumerable))) 138 end 139 140 class C2 141 def initialize(ary) 142 @ary = ary 143 end 144 def _dump(s) 145 @ary.clear 146 "foo" 147 end 148 end 149 150 def test_modify_array_during_dump 151 a = [] 152 o = C2.new(a) 153 a << o << nil 154 assert_raise(RuntimeError) { Marshal.dump(a) } 155 end 156 157 def test_change_class_name 158 eval("class C3; def _dump(s); 'foo'; end; end") 159 m = Marshal.dump(C3.new) 160 assert_raise(TypeError) { Marshal.load(m) } 161 eval("C3 = nil") 162 assert_raise(TypeError) { Marshal.load(m) } 163 end 164 165 def test_change_struct 166 eval("C3 = Struct.new(:foo, :bar)") 167 m = Marshal.dump(C3.new("FOO", "BAR")) 168 eval("C3 = Struct.new(:foo)") 169 assert_raise(TypeError) { Marshal.load(m) } 170 eval("C3 = Struct.new(:foo, :baz)") 171 assert_raise(TypeError) { Marshal.load(m) } 172 end 173 174 class C4 175 def initialize(gc) 176 @gc = gc 177 end 178 def _dump(s) 179 GC.start if @gc 180 "foo" 181 end 182 end 183 184 def test_gc 185 assert_nothing_raised do 186 Marshal.dump((0..1000).map {|x| C4.new(x % 50 == 25) }) 187 end 188 end 189 190 def test_taint_and_untrust 191 x = Object.new 192 x.taint 193 x.untrust 194 s = Marshal.dump(x) 195 assert_equal(true, s.tainted?) 196 assert_equal(true, s.untrusted?) 197 y = Marshal.load(s) 198 assert_equal(true, y.tainted?) 199 assert_equal(true, y.untrusted?) 200 end 201 202 def test_taint_and_untrust_each_object 203 x = Object.new 204 obj = [[x]] 205 206 # clean object causes crean stream 207 assert_equal(false, obj.tainted?) 208 assert_equal(false, obj.untrusted?) 209 assert_equal(false, obj.first.tainted?) 210 assert_equal(false, obj.first.untrusted?) 211 assert_equal(false, obj.first.first.tainted?) 212 assert_equal(false, obj.first.first.untrusted?) 213 s = Marshal.dump(obj) 214 assert_equal(false, s.tainted?) 215 assert_equal(false, s.untrusted?) 216 217 # tainted/untrusted object causes tainted/untrusted stream 218 x.taint 219 x.untrust 220 assert_equal(false, obj.tainted?) 221 assert_equal(false, obj.untrusted?) 222 assert_equal(false, obj.first.tainted?) 223 assert_equal(false, obj.first.untrusted?) 224 assert_equal(true, obj.first.first.tainted?) 225 assert_equal(true, obj.first.first.untrusted?) 226 t = Marshal.dump(obj) 227 assert_equal(true, t.tainted?) 228 assert_equal(true, t.untrusted?) 229 230 # clean stream causes clean objects 231 assert_equal(false, s.tainted?) 232 assert_equal(false, s.untrusted?) 233 y = Marshal.load(s) 234 assert_equal(false, y.tainted?) 235 assert_equal(false, y.untrusted?) 236 assert_equal(false, y.first.tainted?) 237 assert_equal(false, y.first.untrusted?) 238 assert_equal(false, y.first.first.tainted?) 239 assert_equal(false, y.first.first.untrusted?) 240 241 # tainted/untrusted stream causes tainted/untrusted objects 242 assert_equal(true, t.tainted?) 243 assert_equal(true, t.untrusted?) 244 y = Marshal.load(t) 245 assert_equal(true, y.tainted?) 246 assert_equal(true, y.untrusted?) 247 assert_equal(true, y.first.tainted?) 248 assert_equal(true, y.first.untrusted?) 249 assert_equal(true, y.first.first.tainted?) 250 assert_equal(true, y.first.first.untrusted?) 251 252 # same tests by different senario 253 s.taint 254 s.untrust 255 assert_equal(true, s.tainted?) 256 assert_equal(true, s.untrusted?) 257 y = Marshal.load(s) 258 assert_equal(true, y.tainted?) 259 assert_equal(true, y.untrusted?) 260 assert_equal(true, y.first.tainted?) 261 assert_equal(true, y.first.untrusted?) 262 assert_equal(true, y.first.first.tainted?) 263 assert_equal(true, y.first.first.untrusted?) 264 end 265 266 def test_symbol2 267 [:ruby, :"\u{7d05}\u{7389}"].each do |sym| 268 assert_equal(sym, Marshal.load(Marshal.dump(sym)), '[ruby-core:24788]') 269 end 270 bug2548 = '[ruby-core:27375]' 271 ary = [:$1, nil] 272 assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug2548) 273 end 274 275 ClassUTF8 = eval("class R\u{e9}sum\u{e9}; self; end") 276 277 iso_8859_1 = Encoding::ISO_8859_1 278 279 structISO8859_1 = Struct.new("r\xe9sum\xe9".force_encoding(iso_8859_1).intern) 280 const_set("R\xe9sum\xe9".force_encoding(iso_8859_1), structISO8859_1) 281 structISO8859_1.name 282 StructISO8859_1 = structISO8859_1 283 classISO8859_1 = Class.new do 284 attr_accessor "r\xe9sum\xe9".force_encoding(iso_8859_1) 285 eval("def initialize(x) @r\xe9sum\xe9 = x; end".force_encoding(iso_8859_1)) 286 end 287 const_set("R\xe9sum\xe92".force_encoding(iso_8859_1), classISO8859_1) 288 classISO8859_1.name 289 ClassISO8859_1 = classISO8859_1 290 291 def test_class_nonascii 292 a = ClassUTF8.new 293 assert_instance_of(ClassUTF8, Marshal.load(Marshal.dump(a)), '[ruby-core:24790]') 294 295 bug1932 = '[ruby-core:24882]' 296 297 a = StructISO8859_1.new(10) 298 assert_nothing_raised(bug1932) do 299 assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932) 300 end 301 a.__send__("#{StructISO8859_1.members[0]}=", a) 302 assert_nothing_raised(bug1932) do 303 assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932) 304 end 305 306 a = ClassISO8859_1.new(10) 307 assert_nothing_raised(bug1932) do 308 b = Marshal.load(Marshal.dump(a)) 309 assert_equal(ClassISO8859_1, b.class, bug1932) 310 assert_equal(a.instance_variables, b.instance_variables, bug1932) 311 a.instance_variables.each do |i| 312 assert_equal(a.instance_variable_get(i), b.instance_variable_get(i), bug1932) 313 end 314 end 315 a.__send__(a.methods(true).grep(/=\z/)[0], a) 316 assert_nothing_raised(bug1932) do 317 b = Marshal.load(Marshal.dump(a)) 318 assert_equal(ClassISO8859_1, b.class, bug1932) 319 assert_equal(a.instance_variables, b.instance_variables, bug1932) 320 assert_equal(b, b.instance_variable_get(a.instance_variables[0]), bug1932) 321 end 322 end 323 324 def test_regexp2 325 assert_equal(/\\u/, Marshal.load("\004\b/\b\\\\u\000")) 326 assert_equal(/u/, Marshal.load("\004\b/\a\\u\000")) 327 assert_equal(/u/, Marshal.load("\004\bI/\a\\u\000\006:\016@encoding\"\vEUC-JP")) 328 329 bug2109 = '[ruby-core:25625]' 330 a = "\x82\xa0".force_encoding(Encoding::Windows_31J) 331 b = "\x82\xa2".force_encoding(Encoding::Windows_31J) 332 c = [/#{a}/, /#{b}/] 333 assert_equal(c, Marshal.load(Marshal.dump(c)), bug2109) 334 335 assert_nothing_raised(ArgumentError, '[ruby-dev:40386]') do 336 re = Tempfile.open("marshal_regexp") do |f| 337 f.binmode.write("\x04\bI/\x00\x00\x06:\rencoding\"\rUS-ASCII") 338 f.close 339 re2 = Marshal.load(f.open.binmode) 340 f.close(true) 341 re2 342 end 343 assert_equal(//, re) 344 end 345 end 346 347 class DumpTest 348 def marshal_dump 349 @@block.call(:marshal_dump) 350 end 351 352 def dump_each(&block) 353 @@block = block 354 Marshal.dump(self) 355 end 356 end 357 358 class LoadTest 359 def marshal_dump 360 nil 361 end 362 def marshal_load(obj) 363 @@block.call(:marshal_load) 364 end 365 def self.load_each(m, &block) 366 @@block = block 367 Marshal.load(m) 368 end 369 end 370 371 def test_context_switch 372 o = DumpTest.new 373 e = o.enum_for(:dump_each) 374 assert_equal(:marshal_dump, e.next) 375 GC.start 376 assert(true, '[ruby-dev:39425]') 377 assert_raise(StopIteration) {e.next} 378 379 o = LoadTest.new 380 m = Marshal.dump(o) 381 e = LoadTest.enum_for(:load_each, m) 382 assert_equal(:marshal_load, e.next) 383 GC.start 384 assert(true, '[ruby-dev:39425]') 385 assert_raise(StopIteration) {e.next} 386 end 387 388 def test_dump_buffer 389 bug2390 = '[ruby-dev:39744]' 390 w = "" 391 def w.write(str) 392 self << str.to_s 393 end 394 Marshal.dump(Object.new, w) 395 assert_not_empty(w, bug2390) 396 end 397 398 class C5 399 def marshal_dump 400 "foo" 401 end 402 def marshal_load(foo) 403 @foo = foo 404 end 405 def initialize(x) 406 @x = x 407 end 408 end 409 def test_marshal_dump 410 c = C5.new("bar") 411 s = Marshal.dump(c) 412 d = Marshal.load(s) 413 assert_equal("foo", d.instance_variable_get(:@foo)) 414 assert_equal(false, d.instance_variable_defined?(:@x)) 415 end 416 417 class C6 418 def initialize 419 @stdin = STDIN 420 end 421 attr_reader :stdin 422 def marshal_dump 423 1 424 end 425 def marshal_load(x) 426 @stdin = STDIN 427 end 428 end 429 def test_marshal_dump_extra_iv 430 o = C6.new 431 m = nil 432 assert_nothing_raised("[ruby-dev:21475] [ruby-dev:39845]") { 433 m = Marshal.dump(o) 434 } 435 o2 = Marshal.load(m) 436 assert_equal(STDIN, o2.stdin) 437 end 438 439 def test_marshal_string_encoding 440 o1 = ["foo".force_encoding("EUC-JP")] + [ "bar" ] * 2 441 m = Marshal.dump(o1) 442 o2 = Marshal.load(m) 443 assert_equal(o1, o2, "[ruby-dev:40388]") 444 end 445 446 def test_marshal_regexp_encoding 447 o1 = [Regexp.new("r1".force_encoding("EUC-JP"))] + ["r2"] * 2 448 m = Marshal.dump(o1) 449 o2 = Marshal.load(m) 450 assert_equal(o1, o2, "[ruby-dev:40416]") 451 end 452 453 def test_marshal_encoding_encoding 454 o1 = [Encoding.find("EUC-JP")] + ["r2"] * 2 455 m = Marshal.dump(o1) 456 o2 = Marshal.load(m) 457 assert_equal(o1, o2) 458 end 459 460 def test_marshal_symbol_ascii8bit 461 bug6209 = '[ruby-core:43762]' 462 o1 = "\xff".force_encoding("ASCII-8BIT").intern 463 m = Marshal.dump(o1) 464 o2 = nil 465 assert_nothing_raised(EncodingError, bug6209) {o2 = Marshal.load(m)} 466 assert_equal(o1, o2, bug6209) 467 end 468 469 class PrivateClass 470 def initialize(foo) 471 @foo = foo 472 end 473 attr_reader :foo 474 end 475 private_constant :PrivateClass 476 477 def test_marshal_private_class 478 o1 = PrivateClass.new("test") 479 o2 = Marshal.load(Marshal.dump(o1)) 480 assert_equal(o1.class, o2.class) 481 assert_equal(o1.foo, o2.foo) 482 end 483 484 def test_marshal_complex 485 assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x05")} 486 assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x06i\x00")} 487 assert_equal(Complex(1, 2), Marshal.load("\x04\bU:\fComplex[\ai\x06i\a")) 488 assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\bi\x00i\x00i\x00")} 489 end 490 491 def test_marshal_rational 492 assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x05")} 493 assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x06i\x00")} 494 assert_equal(Rational(1, 2), Marshal.load("\x04\bU:\rRational[\ai\x06i\a")) 495 assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\bi\x00i\x00i\x00")} 496 end 497 498 def test_marshal_flonum_reference 499 bug7348 = '[ruby-core:49323]' 500 e = [] 501 ary = [ [2.0, e], [e] ] 502 assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug7348) 503 end 504 505 class TestClass 506 end 507 508 module TestModule 509 end 510 511 def test_marshal_load_should_not_taint_classes 512 bug7325 = '[ruby-core:49198]' 513 for c in [TestClass, TestModule] 514 assert(!c.tainted?) 515 assert(!c.untrusted?) 516 c2 = Marshal.load(Marshal.dump(c).taint.untrust) 517 assert_same(c, c2) 518 assert(!c.tainted?, bug7325) 519 assert(!c.untrusted?, bug7325) 520 end 521 end 522 523 class Bug7627 < Struct.new(:bar) 524 attr_accessor :foo 525 526 def marshal_dump; 'dump'; end # fake dump data 527 def marshal_load(*); end # do nothing 528 end 529 530 def test_marshal_dump_struct_ivar 531 bug7627 = '[ruby-core:51163]' 532 obj = Bug7627.new 533 obj.foo = '[Bug #7627]' 534 535 dump = Marshal.dump(obj) 536 loaded = Marshal.load(dump) 537 538 assert_equal(obj, loaded, bug7627) 539 assert_nil(loaded.foo, bug7627) 540 end 541 542 def test_class_ivar 543 assert_raise(TypeError) {Marshal.load("\x04\x08Ic\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")} 544 assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")} 545 assert_not_operator(TestClass, :instance_variable_defined?, :@bug) 546 end 547 548 def test_module_ivar 549 assert_raise(TypeError) {Marshal.load("\x04\x08Im\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")} 550 assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")} 551 assert_not_operator(TestModule, :instance_variable_defined?, :@bug) 552 end 553 554 class TestForRespondToFalse 555 def respond_to?(a) 556 false 557 end 558 end 559 560 def test_marshal_respond_to_arity 561 assert_nothing_raised(ArgumentError, '[Bug #7722]') do 562 Marshal.dump(TestForRespondToFalse.new) 563 end 564 end 565end 566