node-rdkafka icon indicating copy to clipboard operation
node-rdkafka copied to clipboard

Avoid leaking uv_async_t in Dispatcher::Deactivate()

Open mszabo-wikia opened this issue 10 months ago • 1 comments

On disconnect, Dispatcher::Deactivate() calls uv_close() to close its active uv_async_t instance and nulls out the corresponding field, but never frees the underlying memory.

To reproduce, run the following with node-rdkafka and librdkafka compiled with ASAN:

const { KafkaConsumer } = require('../');

const consumer = new KafkaConsumer({
  'group.id': 'kafka',
  'metadata.broker.list': 'localhost:9092',
}, {});

consumer.connect({ timeout: 2000 }, function (err) {
  if (err) {
    console.error('Error connecting to Kafka:', err);
    return;
  }

  consumer.disconnect();
})

This should report:

Direct leak of 128 byte(s) in 1 object(s) allocated from:
    #0 0x7f049c3aa647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f0471d872a1 in NodeKafka::Callbacks::Dispatcher::Activate() ../src/callbacks.cc:69
    #2 0x7f0471e75c06 in NodeKafka::Workers::KafkaConsumerConnect::HandleOKCallback() ../src/workers.cc:579
    #3 0x7f0471dd679c in Nan::AsyncWorker::WorkComplete() ../node_modules/nan/nan.h:2008
    #4 0x7f0471dd679c in Nan::AsyncExecuteComplete(uv_work_s*) ../node_modules/nan/nan.h:2365
    #5 0x7f0471dd679c in Nan::AsyncExecuteComplete(uv_work_s*, int) ../node_modules/nan/nan.h:2369
    #6 0x18bb75c in uv__work_done ../deps/uv/src/threadpool.c:329
    #7 0x18bf0b2 in uv__async_io ../deps/uv/src/unix/async.c:176
    #8 0x18d3b2a in uv__io_poll ../deps/uv/src/unix/linux.c:1485
    #9 0x18bfdd6 in uv_run ../deps/uv/src/unix/core.c:447
    #10 0xbc9be5 in node::SpinEventLoopInternal(node::Environment*) (/usr/bin/node+0xbc9be5)
    #11 0xd1d920 in node::NodeMainInstance::Run(node::ExitCode*, node::Environment*) [clone .part.0] (/usr/bin/node+0xd1d920)
    #12 0xd1e38c in node::NodeMainInstance::Run() (/usr/bin/node+0xd1e38c)
    #13 0xc710be in node::Start(int, char**) (/usr/bin/node+0xc710be)
    #14 0x7f049bdf0d09 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x23d09)

The libuv documentation says it's only safe to free the underlying memory in a close callback passed to uv_close(), or after such a callback has returned. So, use a unique_ptr with a custom deleter to accomplish this.

mszabo-wikia avatar May 23 '25 23:05 mszabo-wikia