Project

General

Profile

symbol_bug.rb

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

Download (1.92 KB)

 
1
test_symbol = '__test'
2

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

    
8
'__test'.to_sym # breaks it
9
# :__test # does not break it
10

    
11
# GC.start # fixes it
12

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

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

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

30
  def vulnerable_method_2(p: 'p', lost: 'lost', q: 'q', **options)
31
    _report(__method__, lost)
32
  end
33

34
  def immune_method_1(lost: 'lost', p: 'p', **options)
35
    _report(__method__, lost)
36
  end
37

38
  def immune_method_2(q: 'q', lost: 'lost', __test: '__test')
39
    _report(__method__, lost)
40
  end
41

42
  def immune_method_3(lost: 'lost', **options)
43
    _report(__method__, lost)
44
  end
45
RUBY
46

    
47
# Exposure #####################################################################
48

    
49
puts '', 'Broken when calling with a hash'
50
vulnerable_method_1($hash)
51
vulnerable_method_2($hash)
52
immune_method_1($hash)
53
immune_method_2($hash)
54
immune_method_3($hash)
55

    
56
puts '', 'Double splat (**) has no influence:'
57
vulnerable_method_1(**$hash)
58
vulnerable_method_2(**$hash)
59
immune_method_1(**$hash)
60
immune_method_2(**$hash)
61
immune_method_3(**$hash)
62

    
63
puts '', 'Hash order does not matter:'
64
inversed_hash = Hash[$hash.to_a.reverse]
65
vulnerable_method_1(inversed_hash)
66
vulnerable_method_2(inversed_hash)
67
immune_method_1(inversed_hash)
68
immune_method_2(inversed_hash)
69
immune_method_3(inversed_hash)