Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segementation Fault when reading from a closed connection #435

Open
ylansegal opened this issue May 21, 2019 · 3 comments
Open

Segementation Fault when reading from a closed connection #435

ylansegal opened this issue May 21, 2019 · 3 comments

Comments

@ylansegal
Copy link

Thank you for your open source contribution and making this library available. Your work is appreciated!

Environment

Operating System

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.4
BuildVersion:	18E226

TinyTDS Version and Information

$ tsql -C
[TinyTds][v1.0.5][tsql]: /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-1.0.5/exe/tsql
Compile-time settings (established with the "configure" script)
                            Version: freetds v1.00.15
             freetds.conf directory: /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-1.0.5/ports/x86_64-apple-darwin18.2.0/etc
     MS db-lib source compatibility: no
        Sybase binary compatibility: no
                      Thread safety: yes
                      iconv library: yes
                        TDS version: 7.3
                              iODBC: no
                           unixodbc: no
              SSPI "trusted" logins: no
                           Kerberos: no
                            OpenSSL: yes
                             GnuTLS: no
                               MARS: no

FreeTDS Version

$ brew info freetds
freetds: stable 1.1.6 (bottled), HEAD

Description

Reading TinyTds::Results tied to a connection that is closed results in a segmentation fault most of the time, instead of the expected exception.

# repro.rb
require 'tiny_tds'

opts = { host: 'server.com', port: 1433, username: '***', password: '***' }
client = TinyTds::Client.new(opts)
results = client.execute('SELECT 42 as answer_to_life')

client.close
puts results.to_a

My expectation, is that the above code would raise a TinyTds::Error. When I run this multiple times, I see this happens rarely. The most common outcome is a Segmentation Fault:

$ $ for i in {1..20}; do ruby repro.rb 2>&1 | head -n 1; done
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000

An example of the segmentation fault details:

$ ruby repro.rb
repro.rb:8: [BUG] Segmentation fault at 0x0000000000000000
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-darwin18]

-- Crash Report log information --------------------------------------------
   See Crash Report log file under the one of following:
     * ~/Library/Logs/DiagnosticReports
     * /Library/Logs/DiagnosticReports
   for more details.
Don't forget to include the above Crash Report log file in bug reports.

-- Control frame information -----------------------------------------------
c:0004 p:---- s:0017 e:000016 CFUNC  :each
c:0003 p:---- s:0014 E:000ea0 CFUNC  :to_a
c:0002 p:0064 s:0010 E:002318 EVAL   repro.rb:8 [FINISH]
c:0001 p:0000 s:0003 E:001580 (none) [FINISH]

-- Ruby level backtrace information ----------------------------------------
repro.rb:8:in `<main>'
repro.rb:8:in `to_a'
repro.rb:8:in `each'

-- Machine register context ------------------------------------------------
 rax: 0x0000000000000001 rbx: 0x00007f836de01270 rcx: 0x0000000000000001
 rdx: 0x0000000000000000 rdi: 0x00007f836dd9cfa0 rsi: 0x0000000000000000
 rbp: 0x00007ffee3c3ea80 rsp: 0x00007ffee3c3ea40  r8: 0x00000000004b4718
  r9: 0xffffffff00000000 r10: 0x00007f836de00ea8 r11: 0x0000000000000246
 r12: 0x00007f836de016d8 r13: 0x0000000000000000 r14: 0x00007f836dd9cfa0
 r15: 0x6000000000000000 rip: 0x000000010c659fdc rfl: 0x0000000000010206

-- C level backtrace information -------------------------------------------
0   ruby                                0x000000010c1c0077 rb_vm_bugreport + 135
1   ruby                                0x000000010c036873 rb_bug_context + 467
2   ruby                                0x000000010c12e1f1 sigsegv + 81
3   libsystem_platform.dylib            0x00007fff64606b5d _sigtramp + 29
4   libsybdb.5.dylib                    0x000000010c659fdc dbsqlok + 46
5   ruby                                0x000000010c1694d2 rb_thread_call_without_gvl + 82
6   tiny_tds.bundle                     0x000000010c64daa9 rb_tinytds_result_ok_helper + 89
7   tiny_tds.bundle                     0x000000010c64cdaa rb_tinytds_result_each + 458
8   ruby                                0x000000010c1ba07a vm_call0_body + 554
9   ruby                                0x000000010c1a7ae7 rb_call + 247
10  ruby                                0x000000010c1a88ae rb_iterate0 + 206
11  ruby                                0x000000010c1a89bc rb_block_call + 76
12  ruby                                0x000000010c0295d9 enum_to_a + 57
13  ruby                                0x000000010c1b2b67 vm_call_cfunc + 295
14  ruby                                0x000000010c19aca3 vm_exec_core + 12419
15  ruby                                0x000000010c1ad1f0 vm_exec + 144
16  ruby                                0x000000010c040931 ruby_exec_internal + 177
17  ruby                                0x000000010c040828 ruby_run_node + 56
18  ruby                                0x000000010bfc0a7f main + 79

-- Other runtime information -----------------------------------------------

* Loaded script: repro.rb

* Loaded features:

    0 enumerator.so
    1 thread.rb
    2 rational.so
    3 complex.so
    4 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/x86_64-darwin18/enc/encdb.bundle
    5 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/x86_64-darwin18/enc/trans/transdb.bundle
    6 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/x86_64-darwin18/rbconfig.rb
    7 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/compatibility.rb
    8 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/defaults.rb
    9 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/deprecate.rb
   10 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/errors.rb
   11 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/version.rb
   12 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/requirement.rb
   13 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/platform.rb
   14 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/basic_specification.rb
   15 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/stub_specification.rb
   16 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/delegate.rb
   17 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/rfc2396_parser.rb
   18 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/rfc3986_parser.rb
   19 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/common.rb
   20 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/generic.rb
   21 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/ftp.rb
   22 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/http.rb
   23 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/https.rb
   24 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/ldap.rb
   25 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/ldaps.rb
   26 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri/mailto.rb
   27 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/uri.rb
   28 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/specification_policy.rb
   29 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/util/list.rb
   30 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/x86_64-darwin18/stringio.bundle
   31 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/specification.rb
   32 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/exceptions.rb
   33 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/util.rb
   34 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/bundler_version_finder.rb
   35 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/dependency.rb
   36 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_gem.rb
   37 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/monitor.rb
   38 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb
   39 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_warn.rb
   40 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems.rb
   41 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/path_support.rb
   42 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/version.rb
   43 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/core_ext/name_error.rb
   44 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/levenshtein.rb
   45 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/jaro_winkler.rb
   46 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checker.rb
   47 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
   48 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
   49 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/name_error_checkers.rb
   50 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/method_name_checker.rb
   51 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/key_error_checker.rb
   52 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/spell_checkers/null_checker.rb
   53 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean/formatters/plain_formatter.rb
   54 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/did_you_mean-1.2.0/lib/did_you_mean.rb
   55 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/x86_64-darwin18/date_core.bundle
   56 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/date.rb
   57 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/2.5.0/x86_64-darwin18/bigdecimal.bundle
   58 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-2.1.2/lib/tiny_tds/version.rb
   59 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-2.1.2/lib/tiny_tds/error.rb
   60 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-2.1.2/lib/tiny_tds/client.rb
   61 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-2.1.2/lib/tiny_tds/result.rb
   62 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-2.1.2/lib/tiny_tds/gem.rb
   63 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-2.1.2/lib/tiny_tds/tiny_tds.bundle
   64 /Users/ylans/.asdf/installs/ruby/2.5.3/lib/ruby/gems/2.5.0/gems/tiny_tds-2.1.2/lib/tiny_tds.rb

[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html

[IMPORTANT]
Don't forget to include the Crash Report log file under
DiagnosticReports directory in bug reports.

For what it's worth, I can work around the issue by reading all of the results into ruby before closing the connection:

# workaround.rb
require 'tiny_tds'

opts = { host: 'server.com', port: 1433, username: '***', password: '***' }
client = TinyTds::Client.new(opts)
# Notice the "each" called on `TinyTds::Result`
results = client.execute('SELECT 42 as answer_to_life').each

client.close
puts results.to_a
$ for i in {1..20}; do ruby workaround.rb 2>&1 | head -n 1; done
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}
{"answer_to_life"=>42}

Please do let me know if I can provide more information or context.

@ylansegal
Copy link
Author

In addition, I can also reproduce -- to a certain extent -- when the garbage collector runs, even if the connection is not closed explicitly:

# repro.rb

require 'tiny_tds'

class Hitchhiker
  def execute
    opts = { host: 'server.com', port: 1433, username: '***', password: '***' }
    TinyTds::Client.new(opts).execute('SELECT 42 as answer_to_life')
  end
end


dent = Hitchhiker.new
results = dent.execute
GC.start

puts results.to_a
$ for i in {1..20}; do ruby repro.rb 2>&1 | head -n 1; done
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000
repro.rb:15: [BUG] Segmentation fault at 0x0000000000000000

@andyundso
Copy link
Member

I know it's been a while - sorry for never responding to your issue. Many thanks for the detailed report and especially reproduction scripts.

I can confirm this issue still exists, although I consistently get the error message:

$ for i in {1..20}; do ruby repro.rb; done
ruby: util.c:70: tds_set_state: Assertion `tds->state < TDS_VECTOR_SIZE(state_names)' failed.
Aborted (core dumped)
ruby: util.c:70: tds_set_state: Assertion `tds->state < TDS_VECTOR_SIZE(state_names)' failed.
Aborted (core dumped)
ruby: util.c:70: tds_set_state: Assertion `tds->state < TDS_VECTOR_SIZE(state_names)' failed.
Aborted (core dumped)
ruby: util.c:70: tds_set_state: Assertion `tds->state < TDS_VECTOR_SIZE(state_names)' failed.
Aborted (core dumped)
ruby: util.c:70: tds_set_state: Assertion `tds->state < TDS_VECTOR_SIZE(state_names)' failed.
Aborted (core dumped)
ruby: util.c:70: tds_set_state: Assertion `tds->state < TDS_VECTOR_SIZE(state_names)' failed.
Aborted (core dumped)

I used Ruby v3.3.6.

Fixing this scenario should be somewhat straight-forward, as we track internally whether the connection was closed or not.

For the second scenario with the GC I get a mix of error messages:

$ for i in {1..20}; do ruby repro.rb; done
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
ruby: util.c:70: tds_set_state: Assertion `tds->state < TDS_VECTOR_SIZE(state_names)' failed.
Aborted (core dumped)
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
corrupted double-linked list
Aborted (core dumped)
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb:24:in `each': DBPROCESS is dead or not enabled (TinyTds::Error)
	from repro.rb:24:in `to_a'
	from repro.rb:24:in `<main>'
repro.rb: [BUG] Segmentation fault at 0x00005c1d0000f658
ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [x86_64-linux]

-- Control frame information -----------------------------------------------
c:0001 p:0000 s:0003 E:000440 DUMMY  [FINISH]


-- Threading information ---------------------------------------------------
Total ractor count: 1
Ruby thread count for this ractor: 1

-- Machine register context ------------------------------------------------
 RIP: 0x000078be63ea1743 RBP: 0x00005c1d15017760 RSP: 0x00007ffed18a5ea0
 RAX: 0x00005c1d0000f640 RBX: 0x0000000000000710 RCX: 0x0000000000000041
 RDX: 0x000078be6401ad10 RDI: 0x00005c1d15017760 RSI: 0x00005c1d0000f640
  R8: 0x00005c1d14dcbbe0  R9: 0x0000000000000000 R10: 0x0000000000000040
 R11: 0x0000000000000246 R12: 0x000078be6401ac80 R13: 0x0000000000000000
 R14: 0x00005c1d15017e70 R15: 0x00000000000000a0 EFL: 0x0000000000010246

-- C level backtrace information -------------------------------------------

My suspicion is that the GC sweeps something away it should not - likely some state associated with FreeTDS.

I'll take a detailed look at this someday. Having the reproduction scripts definitely helps to write tests for it.

@andyundso
Copy link
Member

so I found the first bug and was able to fix it, see andyundso@2dd9313.

the second one ... difficult.

so, the Result and Client class share the same underlying C structures. what happens in your second reproduction script is that the Client instance gets garbage-collected (because no longer referenced). However, when trying to fetch the result, the Result class still has a pointer to the userdata object which is hold by the Client class - which was just collected - thus leading to a segfault.

There is two options: Once we were able to send the SQL query to the server, we give the Result object control over the userdata as well as the FreeTDS client. This likely requires a new C struct. From the Client, we tell the result about the pointer to said struct and forgot about it in the Client class (or rather initialize a new one with connect).

... or, we just stop lazy-loading data for each and implement what I suggest at some point in this comment. which is making the Result class a pure Ruby thing, so that Client is the only one messing around with C code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants