Bug #20932
closedSocket fast_fallback segfaults when fds are > FD_SETSIZE
Description
When Socket.tcp_fast_fallback = true
and a socket ends up with an FD over FD_SETSIZE (typically 1024), it results in a buffer overflow and crashes when running FD_SET before select.
It may be necessary to update ulimits to be above 1024 before reproducing (ulimit -n 2048
)
require "socket"
open_fds = []
loop do
file = open(__FILE__)
open_fds << file
break if file.fileno >= 1010
end
#Socket.tcp_fast_fallback=false
TCPServer.open("localhost", 0) do |server|
port = server.addr[1]
sockets = []
50.times do |i|
socket = TCPSocket.open("localhost", port)
p socket
sockets << socket
end
end
$ ulimit -n 2048
$ ruby test.rb
#<TCPSocket:fd 1015, AF_INET6, ::1, 40622>
#<TCPSocket:fd 1014, AF_INET6, ::1, 40628>
#<TCPSocket:fd 1016, AF_INET6, ::1, 40630>
#<TCPSocket:fd 1017, AF_INET6, ::1, 40632>
#<TCPSocket:fd 1018, AF_INET6, ::1, 40644>
#<TCPSocket:fd 1020, AF_INET6, ::1, 40658>
#<TCPSocket:fd 1021, AF_INET6, ::1, 40668>
#<TCPSocket:fd 1019, AF_INET6, ::1, 40674>
#<TCPSocket:fd 1022, AF_INET6, ::1, 40678>
#<TCPSocket:fd 1023, AF_INET6, ::1, 40694>
=================================================================
==3180778==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ae35aadc580 at pc 0x7ae35b1b7449 bp 0x7fffc9af1af0 sp 0x7fffc9af1ae8
READ of size 8 at 0x7ae35aadc580 thread T0
#0 0x7ae35b1b7448 in init_fast_fallback_inetsock_internal /home/jhawthorn/src/ruby/ext/socket/ipsocket.c:912:17
#1 0x60462ff14879 in rb_ensure /home/jhawthorn/src/ruby/eval.c:1053:18
#2 0x7ae35b1b41b9 in rsock_init_inetsock /home/jhawthorn/src/ruby/ext/socket/ipsocket.c:1315:20
#3 0x7ae35b1bae9a in tcp_init /home/jhawthorn/src/ruby/ext/socket/tcpsocket.c:91:12
#4 0x604630178944 in vm_call0_cfunc_with_frame /home/jhawthorn/src/ruby/./vm_eval.c:164:15
#5 0x604630176ade in vm_call0_body /home/jhawthorn/src/ruby/./vm_eval.c:229:15
#6 0x604630146e46 in vm_call0_cc /home/jhawthorn/src/ruby/./vm_eval.c:101:12
#7 0x604630178ffd in rb_call0 /home/jhawthorn/src/ruby/./vm_eval.c:554:12
#8 0x604630147e3a in rb_call /home/jhawthorn/src/ruby/./vm_eval.c:873:12
#9 0x60462ffe15cd in rb_class_new_instance_kw /home/jhawthorn/src/ruby/object.c:2187:5
#10 0x60462ff6c39c in rb_io_s_open /home/jhawthorn/src/ruby/io.c:8126:16
#11 0x604630166e9c in vm_call_cfunc_with_frame_ /home/jhawthorn/src/ruby/./vm_insnhelper.c:3801:11
#12 0x604630134dad in vm_sendish /home/jhawthorn/src/ruby/./vm_insnhelper.c
#13 0x60463013c68b in vm_exec_core /home/jhawthorn/src/ruby/insns.def:898:11
#14 0x604630135327 in rb_vm_exec /home/jhawthorn/src/ruby/vm.c:2585:22
#15 0x60463017ba7c in invoke_iseq_block_from_c /home/jhawthorn/src/ruby/vm.c:1615:12
#16 0x60463017ba7c in invoke_block_from_c_bh /home/jhawthorn/src/ruby/vm.c:1629:20
#17 0x60463014b477 in vm_yield_with_cref /home/jhawthorn/src/ruby/vm.c:1666:12
#18 0x604630148937 in rb_yield /home/jhawthorn/src/ruby/./vm_eval.c
#19 0x60462ff14879 in rb_ensure /home/jhawthorn/src/ruby/eval.c:1053:18
#20 0x604630166e9c in vm_call_cfunc_with_frame_ /home/jhawthorn/src/ruby/./vm_insnhelper.c:3801:11
#21 0x604630159de6 in vm_call_method_each_type /home/jhawthorn/src/ruby/./vm_insnhelper.c:4779:16
#22 0x604630159afe in vm_call_method /home/jhawthorn/src/ruby/./vm_insnhelper.c
#23 0x604630134dad in vm_sendish /home/jhawthorn/src/ruby/./vm_insnhelper.c
#24 0x60463013ba52 in vm_exec_core /home/jhawthorn/src/ruby/insns.def:851:11
#25 0x60463015113b in vm_exec_loop /home/jhawthorn/src/ruby/vm.c:2612:22
#26 0x60463013533a in rb_vm_exec /home/jhawthorn/src/ruby/vm.c
#27 0x60462ff12f0c in rb_ec_exec_node /home/jhawthorn/src/ruby/eval.c:281:9
#28 0x60462ff12d12 in ruby_run_node /home/jhawthorn/src/ruby/eval.c:319:30
#29 0x60462ff0ed73 in rb_main /home/jhawthorn/src/ruby/./main.c:43:12
#30 0x60462ff0ec47 in main /home/jhawthorn/src/ruby/./main.c:68:12
#31 0x7ae35c5e7e07 in __libc_start_call_main /usr/src/debug/glibc/glibc/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#32 0x7ae35c5e7ecb in __libc_start_main /usr/src/debug/glibc/glibc/csu/../csu/libc-start.c:360:3
#33 0x60462fe2f334 in _start (/home/jhawthorn/.rubies/ruby-asan/bin/ruby+0x174334)
Address 0x7ae35aadc580 is located in stack of thread T0 at offset 384 in frame
#0 0x7ae35b1b44df in init_fast_fallback_inetsock_internal /home/jhawthorn/src/ruby/ext/socket/ipsocket.c:537
This frame has 14 object(s):
[32, 36) 'status' (line 544)
[48, 50) 'resolved_type' (line 555)
[64, 72) 'pipefd' (line 558)
[96, 224) 'readfds' (line 559)
[256, 384) 'writefds' (line 559) <== Memory access at offset 384 overflows this variable
[416, 456) 'wait_arg' (line 561)
[496, 512) 'delay' (line 563)
[528, 568) 'resolution_store' (line 567)
[608, 624) 'resolution_delay_storage' (line 604)
[640, 656) 'connection_attempt_delay_strage' (line 606)
[672, 688) 'user_specified_resolv_timeout_storage' (line 608)
[704, 720) 'user_specified_connect_timeout_storage' (line 610)
[736, 740) 'err' (line 953)
[752, 756) 'len' (line 954)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/jhawthorn/src/ruby/ext/socket/ipsocket.c:912:17 in init_fast_fallback_inetsock_internal
Shadow bytes around the buggy address:
0x7ae35aadc300: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7ae35aadc380: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7ae35aadc400: f1 f1 f1 f1 04 f2 02 f2 00 f2 f2 f2 00 00 00 00
0x7ae35aadc480: 00 00 00 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2
0x7ae35aadc500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7ae35aadc580:[f2]f2 f2 f2 00 00 00 00 00 f2 f2 f2 f2 f2 00 00
0x7ae35aadc600: f2 f2 00 00 00 00 00 f2 f2 f2 f2 f2 00 00 f2 f2
0x7ae35aadc680: 00 00 f2 f2 00 00 f2 f2 00 00 f2 f2 f8 f2 f8 f3
0x7ae35aadc700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7ae35aadc780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7ae35aadc800: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==3180778==ABORTING
Updated by jhawthorn (John Hawthorn) 16 days ago
- Backport changed from 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN to 3.1: DONTNEED, 3.2: DONTNEED, 3.3: DONTNEED
Updated by shioimm (Misaki Shioi) 15 days ago
- Status changed from Open to Assigned
I really appreciate your report.
I am considering using poll(2) or rb_thread_fd_select
instead of select(2) for this issue.
(Since this feature currently lacks an implementation for Windows, choosing the former shouldn't pose a major problem.)
In any case, it seems the implementation will require a fair amount of modification.
Updated by shioimm (Misaki Shioi) 10 days ago
- Status changed from Assigned to Closed
I believe this issue has been resolved with this PR: https://github.com/ruby/ruby/pull/12292.
If you have any concerns, please let me know.
Thank you again for reporting it.