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

Thread safety issue with PubSub and hiredis #208

Closed
rianmcguire opened this issue Jul 30, 2024 · 2 comments · Fixed by #209
Closed

Thread safety issue with PubSub and hiredis #208

rianmcguire opened this issue Jul 30, 2024 · 2 comments · Fixed by #209

Comments

@rianmcguire
Copy link
Contributor

I've been digging into some Ruby crashes we've been seeing with ActionCable in our test suite, and I think I've uncovered a thread safety issue when HiredisConnection is used with #pubsub.

Minimal reproduction:

require 'redis-client'
require 'hiredis-client'

redis_config = RedisClient.config
redis = redis_config.new_client
pubsub = redis.pubsub

reader = Thread.new do
    while event = pubsub.next_event
    end
end

loop do
    pubsub.call_v(["subscribe", "channel"])
    pubsub.call_v(["unsubscribe", "channel"])
end

I understand using multiple threads here is supposed to be safe, as PubSub #call_v only writes to the socket, and #next_event only reads from the socket (see also redis/redis-rb#1131).

Without hiredis-client, this runs indefinitely without errors.

With hiredis-client, it either fails with a weird connection error:

/var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:113:in `rescue in write': Connection reset by peer (redis://localhost:6379) (RedisClient::ConnectionError)
	from /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:109:in `write'
	from /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client.rb:498:in `call_v'
	from repro.rb:17:in `block in <main>'
	from repro.rb:15:in `loop'
	from repro.rb:15:in `<main>'
/var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:111:in `flush': Connection reset by peer (Errno::ECONNRESET)
	from /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:111:in `write'
	from /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client.rb:498:in `call_v'
	from repro.rb:17:in `block in <main>'
	from repro.rb:15:in `loop'
	from repro.rb:15:in `<main>'

Or crashes Ruby with a seg fault:

Ruby crash dump

/var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:111: [BUG] Segmentation fault at 0x0000000000000008
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [aarch64-linux-gnu]

-- Control frame information -----------------------------------------------
c:0007 p:---- s:0032 e:000031 CFUNC  :flush
c:0006 p:0009 s:0028 e:000027 METHOD /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:111
c:0005 p:0012 s:0022 e:000021 METHOD /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client.rb:498
c:0004 p:0010 s:0017 e:000016 BLOCK  repro.rb:16 [FINISH]
c:0003 p:---- s:0014 e:000013 CFUNC  :loop
c:0002 p:0055 s:0010 E:001610 EVAL   repro.rb:15 [FINISH]
c:0001 p:0000 s:0003 E:000030 (none) [FINISH]

-- Ruby level backtrace information ----------------------------------------
repro.rb:15:in `<main>'
repro.rb:15:in `loop'
repro.rb:16:in `block in <main>'
/var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client.rb:498:in `call_v'
/var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:111:in `write'
/var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb:111:in `flush'

-- Machine register context ------------------------------------------------
  x0: 0x0000000000000000  x1: 0x0000fffff9875bcc  x2: 0x0000000000000000
  x3: 0x0000000000000001  x4: 0x0000000000000000  x5: 0x0000aaaaffac0170
  x6: 0x0000000000000000  x7: 0x000000000040b79e x18: 0x0000ffffb2b55670
 x19: 0x0000fffff9875bd0 x20: 0x0000000000000000 x21: 0x0000000000000001
 x22: 0x0000ffffaec13054 x23: 0x0000fffff9875bd0 x24: 0x0000aaaaffabfd80
 x25: 0x0000aaaaffabc0f0 x26: 0x0000aaaaffabd310 x27: 0x0000000000000000
 x28: 0x0000000000000000 x29: 0x0000fffff9875ab0  sp: 0x0000fffff9875ab0
 fau: 0x0000000000000008

-- C level backtrace information -------------------------------------------
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(0xffffb3011ecc) [0xffffb3011ecc]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(0xffffb2e6c854) [0xffffb2e6c854]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(0xffffb2f88eb8) [0xffffb2f88eb8]
linux-vdso.so.1(__kernel_rt_sigreturn+0x0) [0xffffb319e7bc]
[0xffffaec1924c]
[0xffffaec1306c]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(rb_nogvl+0xc0) [0xffffb2fc2a74]
[0xffffaec152cc]
[0xffffb2ff67a4]
[0xffffb2ff8bc0]
[0xffffb2ffb74c]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(rb_vm_exec+0xd4) [0xffffb2ffffd4]
[0xffffb300314c]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(rb_vrescue2+0xec) [0xffffb2e73a3c]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(rb_rescue2+0x78) [0xffffb2e73c6c]
[0xffffb2ff67a4]
[0xffffb2ff8bc0]
[0xffffb2ffb7ac]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(rb_vm_exec+0xd4) [0xffffb2ffffd4]
[0xffffb2e72694]
/lib/aarch64-linux-gnu/libruby-3.1.so.3.1(ruby_run_node+0x68) [0xffffb2e75fc8]
[0xaaaae4410b1c]
[0xffffb2a67780]
[0xffffb2a67858]
/usr/bin/ruby3.1(_start+0x30) [0xaaaae4410bb0]

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

* Loaded script: repro.rb

* Loaded features:

    0 enumerator.so
    1 thread.rb
    2 fiber.so
    3 rational.so
    4 complex.so
    5 ruby2_keywords.rb
    6 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/encdb.so
    7 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/trans/transdb.so
    8 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/rbconfig.rb
    9 /usr/lib/ruby/vendor_ruby/rubygems/compatibility.rb
   10 /usr/lib/ruby/vendor_ruby/rubygems/defaults.rb
   11 /usr/lib/ruby/vendor_ruby/rubygems/deprecate.rb
   12 /usr/lib/ruby/vendor_ruby/rubygems/errors.rb
   13 /usr/lib/ruby/vendor_ruby/rubygems/unknown_command_spell_checker.rb
   14 /usr/lib/ruby/vendor_ruby/rubygems/exceptions.rb
   15 /usr/lib/ruby/vendor_ruby/rubygems/basic_specification.rb
   16 /usr/lib/ruby/vendor_ruby/rubygems/stub_specification.rb
   17 /usr/lib/ruby/vendor_ruby/rubygems/platform.rb
   18 /usr/lib/ruby/vendor_ruby/rubygems/version.rb
   19 /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb
   20 /usr/lib/ruby/vendor_ruby/rubygems/util/list.rb
   21 /usr/lib/ruby/vendor_ruby/rubygems/specification.rb
   22 /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
   23 /usr/lib/ruby/vendor_ruby/rubygems/util.rb
   24 /usr/lib/ruby/vendor_ruby/rubygems/dependency.rb
   25 /usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_gem.rb
   26 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/monitor.so
   27 /usr/lib/ruby/3.1.0/monitor.rb
   28 /usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_require.rb
   29 /usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_warn.rb
   30 /usr/lib/ruby/vendor_ruby/rubygems.rb
   31 /usr/lib/ruby/vendor_ruby/rubygems/path_support.rb
   32 /usr/lib/ruby/3.1.0/error_highlight/version.rb
   33 /usr/lib/ruby/3.1.0/error_highlight/base.rb
   34 /usr/lib/ruby/3.1.0/error_highlight/formatter.rb
   35 /usr/lib/ruby/3.1.0/error_highlight/core_ext.rb
   36 /usr/lib/ruby/3.1.0/error_highlight.rb
   37 /usr/lib/ruby/3.1.0/did_you_mean/version.rb
   38 /usr/lib/ruby/3.1.0/did_you_mean/core_ext/name_error.rb
   39 /usr/lib/ruby/3.1.0/did_you_mean/levenshtein.rb
   40 /usr/lib/ruby/3.1.0/did_you_mean/jaro_winkler.rb
   41 /usr/lib/ruby/3.1.0/did_you_mean/spell_checker.rb
   42 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
   43 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
   44 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/name_error_checkers.rb
   45 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/method_name_checker.rb
   46 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/key_error_checker.rb
   47 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/null_checker.rb
   48 /usr/lib/ruby/3.1.0/did_you_mean/tree_spell_checker.rb
   49 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/require_path_checker.rb
   50 /usr/lib/ruby/3.1.0/did_you_mean/spell_checkers/pattern_key_name_checker.rb
   51 /usr/lib/ruby/3.1.0/did_you_mean/formatter.rb
   52 /usr/lib/ruby/3.1.0/did_you_mean.rb
   53 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/version.rb
   54 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/command_builder.rb
   55 /usr/lib/ruby/3.1.0/uri/version.rb
   56 /usr/lib/ruby/3.1.0/uri/rfc2396_parser.rb
   57 /usr/lib/ruby/3.1.0/uri/rfc3986_parser.rb
   58 /usr/lib/ruby/3.1.0/uri/common.rb
   59 /usr/lib/ruby/3.1.0/uri/generic.rb
   60 /usr/lib/ruby/3.1.0/uri/file.rb
   61 /usr/lib/ruby/3.1.0/uri/ftp.rb
   62 /usr/lib/ruby/3.1.0/uri/http.rb
   63 /usr/lib/ruby/3.1.0/uri/https.rb
   64 /usr/lib/ruby/3.1.0/uri/ldap.rb
   65 /usr/lib/ruby/3.1.0/uri/ldaps.rb
   66 /usr/lib/ruby/3.1.0/uri/mailto.rb
   67 /usr/lib/ruby/3.1.0/uri/ws.rb
   68 /usr/lib/ruby/3.1.0/uri.rb
   69 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/url_config.rb
   70 /usr/lib/ruby/3.1.0/digest/version.rb
   71 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/digest.so
   72 /usr/lib/ruby/3.1.0/digest/loader.rb
   73 /usr/lib/ruby/3.1.0/digest.rb
   74 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/openssl.so
   75 /usr/lib/ruby/3.1.0/openssl/bn.rb
   76 /usr/lib/ruby/3.1.0/openssl/marshal.rb
   77 /usr/lib/ruby/3.1.0/openssl/pkey.rb
   78 /usr/lib/ruby/3.1.0/openssl/cipher.rb
   79 /usr/lib/ruby/3.1.0/openssl/digest.rb
   80 /usr/lib/ruby/3.1.0/openssl/hmac.rb
   81 /usr/lib/ruby/3.1.0/openssl/x509.rb
   82 /usr/lib/ruby/3.1.0/openssl/buffering.rb
   83 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/nonblock.so
   84 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/socket.so
   85 /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/wait.so
   86 /usr/lib/ruby/3.1.0/socket.rb
   87 /usr/lib/ruby/3.1.0/ipaddr.rb
   88 /usr/lib/ruby/3.1.0/openssl/ssl.rb
   89 /usr/lib/ruby/3.1.0/openssl/pkcs5.rb
   90 /usr/lib/ruby/3.1.0/openssl/version.rb
   91 /usr/lib/ruby/3.1.0/openssl.rb
   92 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/config.rb
   93 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/pid_cache.rb
   94 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/sentinel_config.rb
   95 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/middlewares.rb
   96 /usr/lib/ruby/3.1.0/timeout.rb
   97 /var/lib/gems/3.1.0/gems/connection_pool-2.4.1/lib/connection_pool/version.rb
   98 /var/lib/gems/3.1.0/gems/connection_pool-2.4.1/lib/connection_pool/timed_stack.rb
   99 /var/lib/gems/3.1.0/gems/connection_pool-2.4.1/lib/connection_pool/wrapper.rb
  100 /var/lib/gems/3.1.0/gems/connection_pool-2.4.1/lib/connection_pool.rb
  101 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/pooled.rb
  102 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/circuit_breaker.rb
  103 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/connection_mixin.rb
  104 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/buffered_io.rb
  105 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection/resp3.rb
  106 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client/ruby_connection.rb
  107 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis_client.rb
  108 /var/lib/gems/3.1.0/gems/redis-client-0.22.2/lib/redis-client.rb
  109 /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.so
  110 /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.rb
  111 /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/hiredis-client.rb

* Process memory map:

aaaae4410000-aaaae4411000 r-xp 00000000 fe:01 4798                       /usr/bin/ruby3.1
aaaae442f000-aaaae4430000 r--p 0000f000 fe:01 4798                       /usr/bin/ruby3.1
aaaae4430000-aaaae4431000 rw-p 00010000 fe:01 4798                       /usr/bin/ruby3.1
aaaaffabb000-aaab00081000 rw-p 00000000 00:00 0                          [heap]
ffffa8000000-ffffa8021000 rw-p 00000000 00:00 0 
ffffa8021000-ffffac000000 ---p 00000000 00:00 0 
ffffae400000-ffffae782000 r--s 00000000 fe:01 7924                       /usr/lib/aarch64-linux-gnu/libruby-3.1.so.3.1.2
ffffae980000-ffffae994000 r-xp 00000000 fe:01 1599                       /usr/lib/aarch64-linux-gnu/libgcc_s.so.1
ffffae994000-ffffae9af000 ---p 00014000 fe:01 1599                       /usr/lib/aarch64-linux-gnu/libgcc_s.so.1
ffffae9af000-ffffae9b0000 r--p 0001f000 fe:01 1599                       /usr/lib/aarch64-linux-gnu/libgcc_s.so.1
ffffae9b0000-ffffae9b1000 rw-p 00020000 fe:01 1599                       /usr/lib/aarch64-linux-gnu/libgcc_s.so.1
ffffae9b7000-ffffae9c8000 r--s 00000000 fe:01 4798                       /usr/bin/ruby3.1
ffffae9c8000-ffffaea00000 rw-p 00000000 00:00 0 
ffffaea00000-ffffaea10000 ---p 00000000 00:00 0 
ffffaea10000-ffffaec10000 rw-p 00000000 00:00 0 
ffffaec10000-ffffaec27000 r-xp 00000000 fe:01 824845                     /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.so
ffffaec27000-ffffaec3f000 ---p 00017000 fe:01 824845                     /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.so
ffffaec3f000-ffffaec40000 r--p 0001f000 fe:01 824845                     /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.so
ffffaec40000-ffffaec41000 rw-p 00020000 fe:01 824845                     /var/lib/gems/3.1.0/gems/hiredis-client-0.22.2/lib/redis_client/hiredis_connection.so
ffffaec48000-ffffaec50000 rw-p 00000000 00:00 0 
ffffaec50000-ffffaec52000 r-xp 00000000 fe:01 397265                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/wait.so
ffffaec52000-ffffaec6f000 ---p 00002000 fe:01 397265                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/wait.so
ffffaec6f000-ffffaec70000 r--p 0000f000 fe:01 397265                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/wait.so
ffffaec70000-ffffaec71000 rw-p 00010000 fe:01 397265                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/wait.so
ffffaec78000-ffffaec80000 rw-p 00000000 00:00 0 
ffffaec80000-ffffaecae000 r-xp 00000000 fe:01 397284                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/socket.so
ffffaecae000-ffffaecbf000 ---p 0002e000 fe:01 397284                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/socket.so
ffffaecbf000-ffffaecc0000 r--p 0002f000 fe:01 397284                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/socket.so
ffffaecc0000-ffffaecc1000 rw-p 00030000 fe:01 397284                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/socket.so
ffffaecc8000-ffffaecd0000 rw-p 00000000 00:00 0 
ffffaecd0000-ffffaecd1000 r-xp 00000000 fe:01 397264                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/nonblock.so
ffffaecd1000-ffffaecef000 ---p 00001000 fe:01 397264                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/nonblock.so
ffffaecef000-ffffaecf0000 r--p 0000f000 fe:01 397264                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/nonblock.so
ffffaecf0000-ffffaecf1000 rw-p 00010000 fe:01 397264                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/io/nonblock.so
ffffaecf8000-ffffaed00000 rw-p 00000000 00:00 0 
ffffaed00000-ffffaed04000 r-xp 00000000 fe:01 397195                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/digest.so
ffffaed04000-ffffaed1f000 ---p 00004000 fe:01 397195                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/digest.so
ffffaed1f000-ffffaed20000 r--p 0000f000 fe:01 397195                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/digest.so
ffffaed20000-ffffaed21000 rw-p 00010000 fe:01 397195                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/digest.so
ffffaed28000-ffffaed30000 rw-p 00000000 00:00 0 
ffffaed30000-ffffaf113000 r-xp 00000000 fe:01 2195                       /usr/lib/aarch64-linux-gnu/libcrypto.so.3
ffffaf113000-ffffaf125000 ---p 003e3000 fe:01 2195                       /usr/lib/aarch64-linux-gnu/libcrypto.so.3
ffffaf125000-ffffaf180000 r--p 003e5000 fe:01 2195                       /usr/lib/aarch64-linux-gnu/libcrypto.so.3
ffffaf180000-ffffaf183000 rw-p 00440000 fe:01 2195                       /usr/lib/aarch64-linux-gnu/libcrypto.so.3
ffffaf183000-ffffaf186000 rw-p 00000000 00:00 0 
ffffaf18c000-ffffaf190000 rw-p 00000000 00:00 0 
ffffaf190000-ffffaf22b000 r-xp 00000000 fe:01 2196                       /usr/lib/aarch64-linux-gnu/libssl.so.3
ffffaf22b000-ffffaf236000 ---p 0009b000 fe:01 2196                       /usr/lib/aarch64-linux-gnu/libssl.so.3
ffffaf236000-ffffaf240000 r--p 000a6000 fe:01 2196                       /usr/lib/aarch64-linux-gnu/libssl.so.3
ffffaf240000-ffffaf244000 rw-p 000b0000 fe:01 2196                       /usr/lib/aarch64-linux-gnu/libssl.so.3
ffffaf248000-ffffaf250000 rw-p 00000000 00:00 0 
ffffaf250000-ffffaf2aa000 r-xp 00000000 fe:01 397273                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/openssl.so
ffffaf2aa000-ffffaf2bc000 ---p 0005a000 fe:01 397273                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/openssl.so
ffffaf2bc000-ffffaf2c0000 r--p 0005c000 fe:01 397273                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/openssl.so
ffffaf2c0000-ffffaf2c1000 rw-p 00060000 fe:01 397273                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/openssl.so
ffffaf2c8000-ffffaf320000 rw-p 00000000 00:00 0 
ffffaf320000-ffffaf322000 r-xp 00000000 fe:01 397270                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/monitor.so
ffffaf322000-ffffaf33f000 ---p 00002000 fe:01 397270                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/monitor.so
ffffaf33f000-ffffaf340000 r--p 0000f000 fe:01 397270                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/monitor.so
ffffaf340000-ffffaf341000 rw-p 00010000 fe:01 397270                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/monitor.so
ffffaf344000-ffffaf3a0000 rw-p 00000000 00:00 0 
ffffaf3a0000-ffffaf3a2000 r-xp 00000000 fe:01 397245                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/trans/transdb.so
ffffaf3a2000-ffffaf3bf000 ---p 00002000 fe:01 397245                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/trans/transdb.so
ffffaf3bf000-ffffaf3c0000 r--p 0000f000 fe:01 397245                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/trans/transdb.so
ffffaf3c0000-ffffaf3c1000 rw-p 00010000 fe:01 397245                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/trans/transdb.so
ffffaf3c8000-ffffaf3d0000 rw-p 00000000 00:00 0 
ffffaf3d0000-ffffaf3d2000 r-xp 00000000 fe:01 397201                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/encdb.so
ffffaf3d2000-ffffaf3ef000 ---p 00002000 fe:01 397201                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/encdb.so
ffffaf3ef000-ffffaf3f0000 r--p 0000f000 fe:01 397201                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/encdb.so
ffffaf3f0000-ffffaf3f1000 rw-p 00010000 fe:01 397201                     /usr/lib/aarch64-linux-gnu/ruby/3.1.0/enc/encdb.so
ffffaf3f8000-ffffaf400000 rw-p 00000000 00:00 0 
ffffaf400000-ffffaf401000 ---p 00000000 00:00 0 
ffffaf401000-ffffaf4a2000 rw-p 00000000 00:00 0 
ffffaf4a2000-ffffaf4a3000 ---p 00000000 00:00 0 
ffffaf4a3000-ffffaf544000 rw-p 00000000 00:00 0 
ffffaf544000-ffffaf545000 ---p 00000000 00:00 0 
ffffaf545000-ffffaf5e6000 rw-p 00000000 00:00 0 
ffffaf5e6000-ffffaf5e7000 ---p 00000000 00:00 0 
ffffaf5e7000-ffffaf688000 rw-p 00000000 00:00 0 
ffffaf688000-ffffaf689000 ---p 00000000 00:00 0 
ffffaf689000-ffffaf72a000 rw-p 00000000 00:00 0 
ffffaf72a000-ffffaf72b000 ---p 00000000 00:00 0 
ffffaf72b000-ffffaf7cc000 rw-p 00000000 00:00 0 
ffffaf7cc000-ffffaf7cd000 ---p 00000000 00:00 0 
ffffaf7cd000-ffffaf86e000 rw-p 00000000 00:00 0 
ffffaf86e000-ffffaf86f000 ---p 00000000 00:00 0 
ffffaf86f000-ffffaf910000 rw-p 00000000 00:00 0 
ffffaf910000-ffffaf911000 ---p 00000000 00:00 0 
ffffaf911000-ffffaf9b2000 rw-p 00000000 00:00 0 
ffffaf9b2000-ffffaf9b3000 ---p 00000000 00:00 0 
ffffaf9b3000-ffffafa54000 rw-p 00000000 00:00 0 
ffffafa54000-ffffafa55000 ---p 00000000 00:00 0 
ffffafa55000-ffffafaf6000 rw-p 00000000 00:00 0 
ffffafaf6000-ffffafaf7000 ---p 00000000 00:00 0 
ffffafaf7000-ffffafb98000 rw-p 00000000 00:00 0 
ffffafb98000-ffffafb99000 ---p 00000000 00:00 0 
ffffafb99000-ffffafc3a000 rw-p 00000000 00:00 0 
ffffafc3a000-ffffafc3b000 ---p 00000000 00:00 0 
ffffafc3b000-ffffafcdc000 rw-p 00000000 00:00 0 
ffffafcdc000-ffffafcdd000 ---p 00000000 00:00 0 
ffffafcdd000-ffffafd7e000 rw-p 00000000 00:00 0 
ffffafd7e000-ffffafd7f000 ---p 00000000 00:00 0 
ffffafd7f000-ffffafe20000 rw-p 00000000 00:00 0 
ffffafe20000-ffffafe21000 ---p 00000000 00:00 0 
ffffafe21000-ffffafec2000 rw-p 00000000 00:00 0 
ffffafec2000-ffffafec3000 ---p 00000000 00:00 0 
ffffafec3000-ffffaff64000 rw-p 00000000 00:00 0 
ffffaff64000-ffffaff65000 ---p 00000000 00:00 0 
ffffaff65000-ffffb0006000 rw-p 00000000 00:00 0 
ffffb0006000-ffffb0007000 ---p 00000000 00:00 0 
ffffb0007000-ffffb00a8000 rw-p 00000000 00:00 0 
ffffb00a8000-ffffb00a9000 ---p 00000000 00:00 0 
ffffb00a9000-ffffb014a000 rw-p 00000000 00:00 0 
ffffb014a000-ffffb014b000 ---p 00000000 00:00 0 
ffffb014b000-ffffb01ec000 rw-p 00000000 00:00 0 
ffffb01ec000-ffffb01ed000 ---p 00000000 00:00 0 
ffffb01ed000-ffffb028e000 rw-p 00000000 00:00 0 
ffffb028e000-ffffb028f000 ---p 00000000 00:00 0 
ffffb028f000-ffffb0330000 rw-p 00000000 00:00 0 
ffffb0330000-ffffb0331000 ---p 00000000 00:00 0 
ffffb0331000-ffffb03d2000 rw-p 00000000 00:00 0 
ffffb03d2000-ffffb03d3000 ---p 00000000 00:00 0 
ffffb03d3000-ffffb0474000 rw-p 00000000 00:00 0 
ffffb0474000-ffffb0475000 ---p 00000000 00:00 0 
ffffb0475000-ffffb0516000 rw-p 00000000 00:00 0 
ffffb0516000-ffffb0517000 ---p 00000000 00:00 0 
ffffb0517000-ffffb05b8000 rw-p 00000000 00:00 0 
ffffb05b8000-ffffb05b9000 ---p 00000000 00:00 0 
ffffb05b9000-ffffb065a000 rw-p 00000000 00:00 0 
ffffb065a000-ffffb065b000 ---p 00000000 00:00 0 
ffffb065b000-ffffb06fc000 rw-p 00000000 00:00 0 
ffffb06fc000-ffffb06fd000 ---p 00000000 00:00 0 
ffffb06fd000-ffffb079e000 rw-p 00000000 00:00 0 
ffffb079e000-ffffb079f000 ---p 00000000 00:00 0 
ffffb079f000-ffffb2840000 rw-p 00000000 00:00 0 
ffffb2847000-ffffb29e9000 rw-p 00000000 00:00 0 
ffffb29e9000-ffffb2a40000 r--p 00000000 fe:01 4298                       /usr/lib/locale/C.utf8/LC_CTYPE
ffffb2a40000-ffffb2bc7000 r-xp 00000000 fe:01 25337                      /usr/lib/aarch64-linux-gnu/libc.so.6
ffffb2bc7000-ffffb2bdc000 ---p 00187000 fe:01 25337                      /usr/lib/aarch64-linux-gnu/libc.so.6
ffffb2bdc000-ffffb2be0000 r--p 0018c000 fe:01 25337                      /usr/lib/aarch64-linux-gnu/libc.so.6
ffffb2be0000-ffffb2be2000 rw-p 00190000 fe:01 25337                      /usr/lib/aarch64-linux-gnu/libc.so.6
ffffb2be2000-ffffb2bef000 rw-p 00000000 00:00 0 
ffffb2bf0000-ffffb2c70000 r-xp 00000000 fe:01 25340                      /usr/lib/aarch64-linux-gnu/libm.so.6
ffffb2c70000-ffffb2c7f000 ---p 00080000 fe:01 25340                      /usr/lib/aarch64-linux-gnu/libm.so.6
ffffb2c7f000-ffffb2c80000 r--p 0008f000 fe:01 25340                      /usr/lib/aarch64-linux-gnu/libm.so.6
ffffb2c80000-ffffb2c81000 rw-p 00090000 fe:01 25340                      /usr/lib/aarch64-linux-gnu/libm.so.6
ffffb2c88000-ffffb2c90000 rw-p 00000000 00:00 0 
ffffb2c90000-ffffb2cbe000 r-xp 00000000 fe:01 1530                       /usr/lib/aarch64-linux-gnu/libcrypt.so.1.1.0
ffffb2cbe000-ffffb2ccf000 ---p 0002e000 fe:01 1530                       /usr/lib/aarch64-linux-gnu/libcrypt.so.1.1.0
ffffb2ccf000-ffffb2cd0000 r--p 0002f000 fe:01 1530                       /usr/lib/aarch64-linux-gnu/libcrypt.so.1.1.0
ffffb2cd0000-ffffb2cd1000 rw-p 00030000 fe:01 1530                       /usr/lib/aarch64-linux-gnu/libcrypt.so.1.1.0
ffffb2cd1000-ffffb2cd9000 rw-p 00000000 00:00 0 
ffffb2ce0000-ffffb2d55000 r-xp 00000000 fe:01 1629                       /usr/lib/aarch64-linux-gnu/libgmp.so.10.4.1
ffffb2d55000-ffffb2d6f000 ---p 00075000 fe:01 1629                       /usr/lib/aarch64-linux-gnu/libgmp.so.10.4.1
ffffb2d6f000-ffffb2d70000 r--p 0007f000 fe:01 1629                       /usr/lib/aarch64-linux-gnu/libgmp.so.10.4.1
ffffb2d70000-ffffb2d71000 rw-p 00080000 fe:01 1629                       /usr/lib/aarch64-linux-gnu/libgmp.so.10.4.1
ffffb2d78000-ffffb2d80000 rw-p 00000000 00:00 0 
ffffb2d80000-ffffb2d9a000 r-xp 00000000 fe:01 4913                       /usr/lib/aarch64-linux-gnu/libz.so.1.2.13
ffffb2d9a000-ffffb2daf000 ---p 0001a000 fe:01 4913                       /usr/lib/aarch64-linux-gnu/libz.so.1.2.13
ffffb2daf000-ffffb2db0000 r--p 0001f000 fe:01 4913                       /usr/lib/aarch64-linux-gnu/libz.so.1.2.13
ffffb2db0000-ffffb2db1000 rw-p 00020000 fe:01 4913                       /usr/lib/aarch64-linux-gnu/libz.so.1.2.13
ffffb2db8000-ffffb2dc0000 rw-p 00000000 00:00 0 
ffffb2dc0000-ffffb312a000 r-xp 00000000 fe:01 7924                       /usr/lib/aarch64-linux-gnu/libruby-3.1.so.3.1.2
ffffb312a000-ffffb3136000 ---p 0036a000 fe:01 7924                       /usr/lib/aarch64-linux-gnu/libruby-3.1.so.3.1.2
ffffb3136000-ffffb3140000 r--p 00376000 fe:01 7924                       /usr/lib/aarch64-linux-gnu/libruby-3.1.so.3.1.2
ffffb3140000-ffffb3141000 rw-p 00380000 fe:01 7924                       /usr/lib/aarch64-linux-gnu/libruby-3.1.so.3.1.2
ffffb3141000-ffffb3152000 rw-p 00000000 00:00 0 
ffffb3158000-ffffb3160000 rw-p 00000000 00:00 0 
ffffb3161000-ffffb3187000 r-xp 00000000 fe:01 25329                      /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffffb318c000-ffffb3190000 rw-p 00000000 00:00 0 
ffffb3193000-ffffb319a000 r--s 00000000 fe:01 25601                      /usr/lib/aarch64-linux-gnu/gconv/gconv-modules.cache
ffffb319a000-ffffb319c000 rw-p 00000000 00:00 0 
ffffb319c000-ffffb319e000 r--p 00000000 00:00 0                          [vvar]
ffffb319e000-ffffb319f000 r-xp 00000000 00:00 0                          [vdso]
ffffb319f000-ffffb31a1000 r--p 0002e000 fe:01 25329                      /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
ffffb31a1000-ffffb31a3000 rw-p 00030000 fe:01 25329                      /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
fffff9079000-fffff9878000 rw-p 00000000 00:00 0                          [stack]


Aborted (core dumped)

With Ruby 3.4.0-preview1 compiled with ASAN, ASAN detects a heap-use-after-free.

Full ASAN report

=================================================================
==99==ERROR: AddressSanitizer: heap-use-after-free on address 0x5070000da3d3 at pc 0xaaaacf167d90 bp 0xffff70e2b5c0 sp 0xffff70e2adb0
READ of size 32 at 0x5070000da3d3 thread T2
    #0 0xaaaacf167d8c in send (/usr/local/bin/ruby+0x97d8c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)
    #1 0xffff70f52024 in redisNetWrite /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/net.c:83:24
    #2 0xffff70f58c88 in redisBufferWrite /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/hiredis.c:971:28
    #3 0xffff70f517b8 in hiredis_buffer_write_safe /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:272:26
    #4 0xffff95930b84 in rb_nogvl /usr/src/ruby/thread.c:1543:5
    #5 0xffff70f4f264 in hiredis_buffer_write_nogvl /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:280:5
    #6 0xffff70f4f264 in hiredis_read_internal /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:745:17
    #7 0xffff70f4f264 in hiredis_read /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:808:13
    #8 0xffff959e9b34 in vm_call_cfunc_with_frame_ /usr/src/ruby/./vm_insnhelper.c:3594:11
    #9 0xffff959ab1fc in vm_sendish /usr/src/ruby/./vm_insnhelper.c:5723:15
    #10 0xffff959ab1fc in vm_exec_core /usr/src/ruby/insns.def:891:11
    #11 0xffff9599f660 in rb_vm_exec /usr/src/ruby/vm.c:2559:22
    #12 0xffff959cd4b8 in invoke_block /usr/src/ruby/vm.c:1517:12
    #13 0xffff959cd4b8 in invoke_iseq_block_from_c /usr/src/ruby/vm.c:1587:16
    #14 0xffff959cd4b8 in invoke_block_from_c_proc /usr/src/ruby/vm.c:1685:16
    #15 0xffff959cd4b8 in vm_invoke_proc /usr/src/ruby/vm.c:1715:12
    #16 0xffff9594685c in thread_do_start_proc /usr/src/ruby/thread.c:594:16
    #17 0xffff959451b8 in thread_do_start /usr/src/ruby/thread.c:611:18
    #18 0xffff959451b8 in thread_start_func_2 /usr/src/ruby/thread.c:666:18
    #19 0xffff959445f4 in call_thread_start_func_2 /usr/src/ruby/./thread_pthread.c:2235:5
    #20 0xffff959445f4 in nt_start /usr/src/ruby/./thread_pthread.c:2280:13
    #21 0xaaaacf19e124 in asan_thread_start(void*) asan_interceptors.cpp.o
    #22 0xffff94fdee54 in start_thread nptl/pthread_create.c:442:8
    #23 0xffff95047f98 in thread_start misc/../sysdeps/unix/sysv/linux/aarch64/clone.S:79

0x5070000da3d3 is located 3 bytes inside of 68-byte region [0x5070000da3d0,0x5070000da414)
freed by thread T0 here:
    #0 0xaaaacf1a057c in free (/usr/local/bin/ruby+0xd057c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)
    #1 0xffff70f58d9c in redisBufferWrite /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/hiredis.c:976:17
    #2 0xffff70f517b8 in hiredis_buffer_write_safe /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:272:26
    #3 0xffff95930b84 in rb_nogvl /usr/src/ruby/thread.c:1543:5
    #4 0x36ffff70f4f6f8  (<unknown module>)
    #5 0xffff959e9b34 in vm_call_cfunc_with_frame_ /usr/src/ruby/./vm_insnhelper.c:3594:11
    #6 0x30ffff959ab1fc  (<unknown module>)
    #7 0x71ffff9599f900  (<unknown module>)
    #8 0x3ffff95651d5c  (<unknown module>)
    #9 0x79ffff95651b94  (<unknown module>)
    #10 0x39aaaacf1dbf1c  (<unknown module>)
    #11 0x10ffff94f8777c  (<unknown module>)
    #12 0xffff94f87854 in __libc_start_main csu/../csu/libc-start.c:360:3
    #13 0xaaaacf10436c in _start (/usr/local/bin/ruby+0x3436c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)

previously allocated by thread T0 here:
    #0 0xaaaacf1a0bbc in realloc (/usr/local/bin/ruby+0xd0bbc) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)
    #1 0xffff70f5c7e4 in hi_realloc /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/./alloc.h:66:12
    #2 0xffff70f5c7e4 in sdsMakeRoomFor /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/sds.c:223:17
    #3 0xffff70f5e338 in sdscatlen /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/sds.c:381:9
    #4 0xffff70f5a27c in __redisAppendCommand /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/hiredis.c:1065:14
    #5 0xffff70f5a27c in redisAppendCommandArgv /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/hiredis.c:1126:9
    #6 0xffff70f4ece8 in hiredis_write /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:684:5
    #7 0xffff959e9b34 in vm_call_cfunc_with_frame_ /usr/src/ruby/./vm_insnhelper.c:3594:11
    #8 0x30ffff959ab1fc  (<unknown module>)
    #9 0x71ffff9599f900  (<unknown module>)
    #10 0x3ffff95651d5c  (<unknown module>)
    #11 0x79ffff95651b94  (<unknown module>)
    #12 0x39aaaacf1dbf1c  (<unknown module>)
    #13 0x10ffff94f8777c  (<unknown module>)
    #14 0xffff94f87854 in __libc_start_main csu/../csu/libc-start.c:360:3
    #15 0xaaaacf10436c in _start (/usr/local/bin/ruby+0x3436c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)

Thread T2 created by T0 here:
    #0 0xaaaacf186538 in pthread_create (/usr/local/bin/ruby+0xb6538) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)
    #1 0xffff9592e504 in native_thread_create0 /usr/src/ruby/./thread_pthread.c:2152:11
    #2 0xffff9592e504 in native_thread_create_dedicated /usr/src/ruby/./thread_pthread.c:2219:12
    #3 0xffff9592e504 in native_thread_create /usr/src/ruby/./thread_pthread.c:2398:16
    #4 0xffff9592e504 in thread_create_core /usr/src/ruby/thread.c:859:11
    #5 0x31ffff9593d77c  (<unknown module>)
    #6 0x4cffff959f8e70  (<unknown module>)
    #7 0x21ffff959fc1b0  (<unknown module>)
    #8 0xaffff9593c28c  (<unknown module>)
    #9 0x42ffff959e9b34  (<unknown module>)
    #10 0x6effff959d8510  (<unknown module>)
    #11 0x3fffff959d7f54  (<unknown module>)
    #12 0x28ffff959a3a80  (<unknown module>)
    #13 0x71ffff9599f900  (<unknown module>)
    #14 0x3ffff95651d5c  (<unknown module>)
    #15 0x79ffff95651b94  (<unknown module>)
    #16 0x39aaaacf1dbf1c  (<unknown module>)
    #17 0x10ffff94f8777c  (<unknown module>)
    #18 0xffff94f87854 in __libc_start_main csu/../csu/libc-start.c:360:3
    #19 0xaaaacf10436c in _start (/usr/local/bin/ruby+0x3436c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)

SUMMARY: AddressSanitizer: heap-use-after-free (/usr/local/bin/ruby+0x97d8c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835) in send
Shadow bytes around the buggy address:
  0x5070000da100: fd fd fa fa fa fa fd fd fd fd fd fd fd fd fd fd
  0x5070000da180: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa
  0x5070000da200: fa fa fd fd fd fd fd fd fd fd fd fa fa fa fa fa
  0x5070000da280: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fd fd
  0x5070000da300: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
=>0x5070000da380: fd fd fd fd fd fa fa fa fa fa[fd]fd fd fd fd fd
  0x5070000da400: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x5070000da480: fd fd fa fa fa fa fd fd fd fd fd fd fd fd fd fd
  0x5070000da500: fa fa fa fa fd fd fd fd fd fd fd fd fd fa fa fa
  0x5070000da580: fa fa fd fd fd fd fd fd fd fd fd fa fa fa fa fa
  0x5070000da600: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fd fd
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
==99==ABORTING

==99==ERROR: AddressSanitizer: heap-use-after-free on address 0x5070000da3d3 at pc 0xaaaacf167d90 bp 0xffff70e2b5c0 sp 0xffff70e2adb0

T2 in the ASAN report is the reader Ruby thread that's calling #next_event:

READ of size 32 at 0x5070000da3d3 thread T2
    #0 0xaaaacf167d8c in send (/usr/local/bin/ruby+0x97d8c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)
    #1 0xffff70f52024 in redisNetWrite /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/net.c:83:24
    #2 0xffff70f58c88 in redisBufferWrite /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/hiredis.c:971:28
    #3 0xffff70f517b8 in hiredis_buffer_write_safe /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:272:26
    #4 0xffff95930b84 in rb_nogvl /usr/src/ruby/thread.c:1543:5
    #5 0xffff70f4f264 in hiredis_buffer_write_nogvl /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:280:5
    #6 0xffff70f4f264 in hiredis_read_internal /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:745:17
    #7 0xffff70f4f264 in hiredis_read /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:808:13

This thread should just be reading, but it ends up accessing the write buffer, as hiredis_read_internal also drains the write buffer.

The write buffer had already been freed by the main Ruby thread that's writing subscribe and unsubscribe commands:

0x5070000da3d3 is located 3 bytes inside of 68-byte region [0x5070000da3d0,0x5070000da414)
freed by thread T0 here:
    #0 0xaaaacf1a057c in free (/usr/local/bin/ruby+0xd057c) (BuildId: 432048382036ad8473f2cd7178b25f9bda061835)
    #1 0xffff70f58d9c in redisBufferWrite /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/vendor/hiredis.c:976:17
    #2 0xffff70f517b8 in hiredis_buffer_write_safe /usr/local/bundle/gems/hiredis-client-0.22.2/ext/redis_client/hiredis/hiredis_connection.c:272:26
    #3 0xffff95930b84 in rb_nogvl /usr/src/ruby/thread.c:1543:5
@rianmcguire
Copy link
Contributor Author

rianmcguire commented Jul 30, 2024

From my naive understanding, it seems flushing the write buffer inside hiredis_read_internal is unnecessary, as HiredisConnection already calls flush after writes?

def write(command)
_write(command)
flush
rescue SystemCallError, IOError => error
raise ConnectionError.with_config(error.message, config)
rescue Error => error
error._set_config(config)
raise error
end
def write_multi(commands)
commands.each do |command|
_write(command)
end
flush
rescue SystemCallError, IOError => error
raise ConnectionError.with_config(error.message, config)
rescue Error => error
error._set_config(config)
raise error
end

Removing the write buffer flush from hiredis_read_internal fixes the crashes/ASAN errors in my reproduction above. I've made a PR with the change.

@byroot
Copy link
Member

byroot commented Jul 30, 2024

Nice catch.

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

Successfully merging a pull request may close this issue.

2 participants