diff options
author | Olek Janiszewski <olek@hoteltonight.com> | 2016-03-11 10:20:41 +0100 |
---|---|---|
committer | Olek Janiszewski <olek@hoteltonight.com> | 2016-03-11 10:35:01 +0100 |
commit | aa0fad51482c24ece58ec7186c45fd340b35ecb1 (patch) | |
tree | dd67264a9b79a9eff6d648a9bdc113885535cd49 /activesupport | |
parent | a101115d5b8011278891f30f69901f9583ea7685 (diff) | |
download | rails-aa0fad51482c24ece58ec7186c45fd340b35ecb1.tar.gz rails-aa0fad51482c24ece58ec7186c45fd340b35ecb1.tar.bz2 rails-aa0fad51482c24ece58ec7186c45fd340b35ecb1.zip |
Prevent `Marshal.load` from looping infinitely
Fix a bug in `Marshal.load` that caused it to loop indefinitely when
trying to autoload a constant that resolved to a different name.
This could occur when marshalling an ActiveRecord 4.0 object (e.g. into
memcached) and then trying to unmarshal it with Rails 4.2. The
marshalled payload contains a reference to
`ActiveRecord::ConnectionAdapters::Mysql2Adapter::Column`, which in
Rails 4.2 resolves to
`ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::Column`.
Diffstat (limited to 'activesupport')
-rw-r--r-- | activesupport/CHANGELOG.md | 5 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/marshal.rb | 5 | ||||
-rw-r--r-- | activesupport/test/core_ext/marshal_test.rb | 11 |
3 files changed, 20 insertions, 1 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index d4d4491ae3..15dc9946c5 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,8 @@ +* Prevent `Marshal.load` from looping infinitely when trying to autoload a constant + which resolves to a different name. + + *Olek Janiszewski* + * Deprecate `Module.local_constants`. Please use `Module.constants(false)` instead. *Yuichiro Kaneko* diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb index e333b26133..ca278cb2fa 100644 --- a/activesupport/lib/active_support/core_ext/marshal.rb +++ b/activesupport/lib/active_support/core_ext/marshal.rb @@ -5,7 +5,10 @@ module ActiveSupport rescue ArgumentError, NameError => exc if exc.message.match(%r|undefined class/module (.+)|) # try loading the class/module - $1.constantize + loaded = $1.constantize + + raise unless $1 == loaded.name + # if it is an IO we need to go back to read the object source.rewind if source.respond_to?(:rewind) retry diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb index 5427837d19..07c0c0d8cb 100644 --- a/activesupport/test/core_ext/marshal_test.rb +++ b/activesupport/test/core_ext/marshal_test.rb @@ -64,6 +64,17 @@ class MarshalTest < ActiveSupport::TestCase end end + test "when one constant resolves to another" do + class Parent; C = Class.new; end + class Child < Parent; C = Class.new; end + + dump = Marshal.dump(Child::C.new) + + Child.send(:remove_const, :C) + + assert_raise(ArgumentError) { Marshal.load(dump) } + end + test "that a real missing class is causing an exception" do dumped = nil with_autoloading_fixtures do |