1# frozen_string_literal: true 2begin 3 require_relative 'helper' 4rescue LoadError 5end 6 7module Fiddle 8 class TestHandle < TestCase 9 include Fiddle 10 11 def test_safe_handle_open 12 Thread.new do 13 $SAFE = 1 14 assert_raise(SecurityError) { 15 Fiddle::Handle.new(LIBC_SO.dup.taint) 16 } 17 end.join 18 ensure 19 $SAFE = 0 20 end 21 22 def test_safe_function_lookup 23 Thread.new do 24 h = Fiddle::Handle.new(LIBC_SO) 25 $SAFE = 1 26 assert_raise(SecurityError) { 27 h["qsort".dup.taint] 28 } 29 end.join 30 ensure 31 $SAFE = 0 32 end 33 34 def test_to_i 35 handle = Fiddle::Handle.new(LIBC_SO) 36 assert_kind_of Integer, handle.to_i 37 end 38 39 def test_static_sym_unknown 40 assert_raise(DLError) { Fiddle::Handle.sym('fooo') } 41 assert_raise(DLError) { Fiddle::Handle['fooo'] } 42 end 43 44 def test_static_sym 45 begin 46 # Linux / Darwin / FreeBSD 47 refute_nil Fiddle::Handle.sym('dlopen') 48 assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen'] 49 return 50 rescue 51 end 52 53 begin 54 # NetBSD 55 require '-test-/dln/empty' 56 refute_nil Fiddle::Handle.sym('Init_empty') 57 assert_equal Fiddle::Handle.sym('Init_empty'), Fiddle::Handle['Init_empty'] 58 return 59 rescue 60 end 61 end unless /mswin|mingw/ =~ RUBY_PLATFORM 62 63 def test_sym_closed_handle 64 handle = Fiddle::Handle.new(LIBC_SO) 65 handle.close 66 assert_raise(DLError) { handle.sym("calloc") } 67 assert_raise(DLError) { handle["calloc"] } 68 end 69 70 def test_sym_unknown 71 handle = Fiddle::Handle.new(LIBC_SO) 72 assert_raise(DLError) { handle.sym('fooo') } 73 assert_raise(DLError) { handle['fooo'] } 74 end 75 76 def test_sym_with_bad_args 77 handle = Handle.new(LIBC_SO) 78 assert_raise(TypeError) { handle.sym(nil) } 79 assert_raise(TypeError) { handle[nil] } 80 end 81 82 def test_sym 83 handle = Handle.new(LIBC_SO) 84 refute_nil handle.sym('calloc') 85 refute_nil handle['calloc'] 86 end 87 88 def test_handle_close 89 handle = Handle.new(LIBC_SO) 90 assert_equal 0, handle.close 91 end 92 93 def test_handle_close_twice 94 handle = Handle.new(LIBC_SO) 95 handle.close 96 assert_raise(DLError) do 97 handle.close 98 end 99 end 100 101 def test_dlopen_returns_handle 102 assert_instance_of Handle, dlopen(LIBC_SO) 103 end 104 105 def test_initialize_noargs 106 handle = Handle.new 107 refute_nil handle['rb_str_new'] 108 end 109 110 def test_initialize_flags 111 handle = Handle.new(LIBC_SO, RTLD_LAZY | RTLD_GLOBAL) 112 refute_nil handle['calloc'] 113 end 114 115 def test_enable_close 116 handle = Handle.new(LIBC_SO) 117 assert !handle.close_enabled?, 'close is enabled' 118 119 handle.enable_close 120 assert handle.close_enabled?, 'close is not enabled' 121 end 122 123 def test_disable_close 124 handle = Handle.new(LIBC_SO) 125 126 handle.enable_close 127 assert handle.close_enabled?, 'close is enabled' 128 handle.disable_close 129 assert !handle.close_enabled?, 'close is enabled' 130 end 131 132 def test_NEXT 133 begin 134 # Linux / Darwin 135 # 136 # There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find 137 # the first occurrence of the desired symbol using the default library search order. The 138 # latter will find the next occurrence of a function in the search order after the current 139 # library. This allows one to provide a wrapper around a function in another shared 140 # library. 141 # --- Ubuntu Linux 8.04 dlsym(3) 142 handle = Handle::NEXT 143 refute_nil handle['malloc'] 144 return 145 rescue 146 end 147 148 begin 149 # BSD 150 # 151 # If dlsym() is called with the special handle RTLD_NEXT, then the search 152 # for the symbol is limited to the shared objects which were loaded after 153 # the one issuing the call to dlsym(). Thus, if the function is called 154 # from the main program, all the shared libraries are searched. If it is 155 # called from a shared library, all subsequent shared libraries are 156 # searched. RTLD_NEXT is useful for implementing wrappers around library 157 # functions. For example, a wrapper function getpid() could access the 158 # "real" getpid() with dlsym(RTLD_NEXT, "getpid"). (Actually, the dlfunc() 159 # interface, below, should be used, since getpid() is a function and not a 160 # data object.) 161 # --- FreeBSD 8.0 dlsym(3) 162 require '-test-/dln/empty' 163 handle = Handle::NEXT 164 refute_nil handle['Init_empty'] 165 return 166 rescue 167 end 168 end unless /mswin|mingw/ =~ RUBY_PLATFORM 169 170 def test_DEFAULT 171 handle = Handle::DEFAULT 172 refute_nil handle['malloc'] 173 end unless /mswin|mingw/ =~ RUBY_PLATFORM 174 175 def test_dlerror 176 # FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls 177 # getaddrinfo(3). And nsdispatch(3) doesn't call dlerror(3) even if 178 # it calls _nss_cache_cycle_prevention_function with dlsym(3). 179 # So our Fiddle::Handle#sym must call dlerror(3) before call dlsym. 180 # In general uses of dlerror(3) should call it before use it. 181 require 'socket' 182 Socket.gethostbyname("localhost") 183 Fiddle.dlopen("/lib/libc.so.7").sym('strcpy') 184 end if /freebsd/=~ RUBY_PLATFORM 185 186 def test_no_memory_leak 187 assert_no_memory_leak(%w[-W0 -rfiddle.so], '', '100_000.times {Fiddle::Handle.allocate}; GC.start', rss: true) 188 end 189 190 if /cygwin|mingw|mswin/ =~ RUBY_PLATFORM 191 def test_fallback_to_ansi 192 k = Fiddle::Handle.new("kernel32.dll") 193 ansi = k["GetFileAttributesA"] 194 assert_equal(ansi, k["GetFileAttributes"], "should fallback to ANSI version") 195 end 196 end 197 end 198end if defined?(Fiddle) 199