Project

General

Profile

Bug #11091 » symbol_bug.rb

ds-makandra (Dominik Schöler), 04/23/2015 02:20 PM

 
test_symbol = '__test'

# The bug only occurs when a symbol used in a keyword argument is dynamically
# added to the Ruby symbols table *before* Ruby first sees the keyword argument.
existing = Symbol.all_symbols.map(&:to_s).grep('__test')
raise "Symbol #{test_symbol} already exists in symbol table!" if existing.any?

'__test'.to_sym # breaks it
# :__test # does not break it

# GC.start # fixes it

# Why #eval?
# Without, Ruby would parse the symbols in this code into its symbol table
# before running the file, which prevents the bug.
eval <<-RUBY
$hash = { __test: '__test', lost: 'lost', q: 'q' }

def _report(name, value)
puts name.to_s << ': ' << (value ? 'ok' : 'broken')
end

# Confirmed broken when:
# - `lost` is the second keyword argument Oo
# - there is a double-splat argument
def vulnerable_method_1(p: 'p', lost: 'lost', **options)
_report(__method__, lost)
end

def vulnerable_method_2(p: 'p', lost: 'lost', q: 'q', **options)
_report(__method__, lost)
end

def immune_method_1(lost: 'lost', p: 'p', **options)
_report(__method__, lost)
end

def immune_method_2(q: 'q', lost: 'lost', __test: '__test')
_report(__method__, lost)
end

def immune_method_3(lost: 'lost', **options)
_report(__method__, lost)
end
RUBY

# Exposure #####################################################################

puts '', 'Broken when calling with a hash'
vulnerable_method_1($hash)
vulnerable_method_2($hash)
immune_method_1($hash)
immune_method_2($hash)
immune_method_3($hash)

puts '', 'Double splat (**) has no influence:'
vulnerable_method_1(**$hash)
vulnerable_method_2(**$hash)
immune_method_1(**$hash)
immune_method_2(**$hash)
immune_method_3(**$hash)

puts '', 'Hash order does not matter:'
inversed_hash = Hash[$hash.to_a.reverse]
vulnerable_method_1(inversed_hash)
vulnerable_method_2(inversed_hash)
immune_method_1(inversed_hash)
immune_method_2(inversed_hash)
immune_method_3(inversed_hash)
(1-1/2)