summaryrefslogtreecommitdiff
path: root/test/fiddle/test_handle.rb
blob: edbfbb3e6eb279cff42c2c4033a1e67aba1c84da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# frozen_string_literal: false
begin
  require_relative 'helper'
rescue LoadError
end

module Fiddle
  class TestHandle < TestCase
    include Fiddle

    include Test::Unit::Assertions

    def test_safe_handle_open
      t = Thread.new do
        $SAFE = 1
        Fiddle::Handle.new(LIBC_SO.taint)
      end
      assert_raise(SecurityError) { t.value }
    end

    def test_safe_function_lookup
      t = Thread.new do
        h = Fiddle::Handle.new(LIBC_SO)
        $SAFE = 1
        h["qsort".taint]
      end
      assert_raise(SecurityError) { t.value }
    end

    def test_to_i
      handle = Fiddle::Handle.new(LIBC_SO)
      assert_kind_of Integer, handle.to_i
    end

    def test_static_sym_unknown
      assert_raise(DLError) { Fiddle::Handle.sym('fooo') }
      assert_raise(DLError) { Fiddle::Handle['fooo'] }
    end

    def test_static_sym
      begin
        # Linux / Darwin / FreeBSD
        refute_nil Fiddle::Handle.sym('dlopen')
        assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen']
        return
      rescue
      end

      begin
        # NetBSD
        require '-test-/dln/empty'
        refute_nil Fiddle::Handle.sym('Init_empty')
        assert_equal Fiddle::Handle.sym('Init_empty'), Fiddle::Handle['Init_empty']
        return
      rescue
      end
    end unless /mswin|mingw/ =~ RUBY_PLATFORM

    def test_sym_closed_handle
      handle = Fiddle::Handle.new(LIBC_SO)
      handle.close
      assert_raise(DLError) { handle.sym("calloc") }
      assert_raise(DLError) { handle["calloc"] }
    end

    def test_sym_unknown
      handle = Fiddle::Handle.new(LIBC_SO)
      assert_raise(DLError) { handle.sym('fooo') }
      assert_raise(DLError) { handle['fooo'] }
    end

    def test_sym_with_bad_args
      handle = Handle.new(LIBC_SO)
      assert_raise(TypeError) { handle.sym(nil) }
      assert_raise(TypeError) { handle[nil] }
    end

    def test_sym
      handle = Handle.new(LIBC_SO)
      refute_nil handle.sym('calloc')
      refute_nil handle['calloc']
    end

    def test_handle_close
      handle = Handle.new(LIBC_SO)
      assert_equal 0, handle.close
    end

    def test_handle_close_twice
      handle = Handle.new(LIBC_SO)
      handle.close
      assert_raise(DLError) do
        handle.close
      end
    end

    def test_dlopen_returns_handle
      assert_instance_of Handle, dlopen(LIBC_SO)
    end

    def test_initialize_noargs
      handle = Handle.new
      refute_nil handle['rb_str_new']
    end

    def test_initialize_flags
      handle = Handle.new(LIBC_SO, RTLD_LAZY | RTLD_GLOBAL)
      refute_nil handle['calloc']
    end

    def test_enable_close
      handle = Handle.new(LIBC_SO)
      assert !handle.close_enabled?, 'close is enabled'

      handle.enable_close
      assert handle.close_enabled?, 'close is not enabled'
    end

    def test_disable_close
      handle = Handle.new(LIBC_SO)

      handle.enable_close
      assert handle.close_enabled?, 'close is enabled'
      handle.disable_close
      assert !handle.close_enabled?, 'close is enabled'
    end

    def test_NEXT
      begin
        # Linux / Darwin
        #
        # There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT.  The  former  will  find
        # the  first  occurrence  of the desired symbol using the default library search order.  The
        # latter will find the next occurrence of a function in the search order after  the  current
        # library.   This  allows  one  to  provide  a  wrapper  around a function in another shared
        # library.
        # --- Ubuntu Linux 8.04 dlsym(3)
        handle = Handle::NEXT
        refute_nil handle['malloc']
        return
      rescue
      end

      begin
        # BSD
        #
        # If dlsym() is called with the special handle RTLD_NEXT, then the search
        # for the symbol is limited to the shared objects which were loaded after
        # the one issuing the call to dlsym().  Thus, if the function is called
        # from the main program, all the shared libraries are searched.  If it is
        # called from a shared library, all subsequent shared libraries are
        # searched.  RTLD_NEXT is useful for implementing wrappers around library
        # functions.  For example, a wrapper function getpid() could access the
        # "real" getpid() with dlsym(RTLD_NEXT, "getpid").  (Actually, the dlfunc()
        # interface, below, should be used, since getpid() is a function and not a
        # data object.)
        # --- FreeBSD 8.0 dlsym(3)
        require '-test-/dln/empty'
        handle = Handle::NEXT
        refute_nil handle['Init_empty']
        return
      rescue
      end
    end unless /mswin|mingw/ =~ RUBY_PLATFORM

    def test_DEFAULT
      handle = Handle::DEFAULT
      refute_nil handle['malloc']
    end unless /mswin|mingw/ =~ RUBY_PLATFORM

    def test_dlerror
      # FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls
      # getaddrinfo(3). And nsdispatch(3) doesn't call dlerror(3) even if
      # it calls _nss_cache_cycle_prevention_function with dlsym(3).
      # So our Fiddle::Handle#sym must call dlerror(3) before call dlsym.
      # In general uses of dlerror(3) should call it before use it.
      require 'socket'
      Socket.gethostbyname("localhost")
      Fiddle.dlopen("/lib/libc.so.7").sym('strcpy')
    end if /freebsd/=~ RUBY_PLATFORM

    def test_no_memory_leak
      assert_no_memory_leak(%w[-W0 -rfiddle.so], '', '100_000.times {Fiddle::Handle.allocate}; GC.start', rss: true)
    end

    if /cygwin|mingw|mswin/ =~ RUBY_PLATFORM
      def test_fallback_to_ansi
        k = Fiddle::Handle.new("kernel32.dll")
        ansi = k["GetFileAttributesA"]
        assert_equal(ansi, k["GetFileAttributes"], "should fallback to ANSI version")
      end
    end
  end
end if defined?(Fiddle)