jjhoughton / napi-ldap

MIT License
7 stars 5 forks source link

Deadlock when using system-ldap-2.6 #19

Open dilyanpalauzov opened 1 year ago

dilyanpalauzov commented 1 year ago

When the bundled libldap.a and liblber.a are utilized they require the function SSL_get_peer() which is a macro in OpenSSL 3 and does not exist in libcrypto.so.3. So I want to use OpenSSL 3 and install napi-ldap by calling

export USE_SYSTEM_LDAP=1 yarn install napi-ldap

My system ldap is OpenLDAP 2.6. With this code

import ldap from 'napi-ldap'
let uri = "a", binddn = 'b', password = 'c'

client = new ldap({
    uri,
            debug: 1,
            timeout: 10000, // = 1min
            ntimeout: 10000, //10s for network timeout
            base: 'dc=aegee,dc=org',
            scope: ldap.BASE,
            connect() {
                console.log('init connect 1')
                this.bind({binddn, password}, x => {
                    console.log('init connect 2')
                    if (x && x.message != 'Success') reject('BIND ERROR ' + x)
                })
                console.log('init connect 3')
            }
}, function (err) {
            console.log('LDAP init err');
            if (err && err.message == 'Timeout') return reinit().then(resolve).catch(reject)
            if (err) return reject('CONNECT ERR ' + err.message)
            retry(client.bind, {binddn, password}).then(resolve).catch(reject)
})

is printed init connect 1. init connect 2 and init connect 3 are not printed. Then the program waits forever in openldap/libraries/libldap/request.c:128. request.c:128 contains LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );

  at 0x74C34BB: futex_wait (futex-internal.h:146)
  by 0x74C34BB: __lll_lock_wait (lowlevellock.c:49)
  by 0x74C95B1: lll_mutex_lock_optimized (pthread_mutex_lock.c:48)
  by 0x74C95B1: pthread_mutex_lock@@GLIBC_2.2.5 (pthread_mutex_lock.c:93)
  by 0x15AA89AE: ldap_send_initial_request (request.c:128)
  by 0x15A9FF39: ldap_sasl_bind (sasl.c:164)
  by 0x15A9FF39: ldap_sasl_bind (sasl.c:136)
  by 0x15AA0674: ldap_simple_bind (sbind.c:81)
  by 0x15A55387: cnx_bind (in /home/vdirsyncer/node_modules/napi-ldap/build/Release/napi_ldap.node)
  by 0x8BD008: v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) (in /usr/local/bin/node)
  by 0xE31638: v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) (in /usr/local/bin/node)
  by 0xE31C05: v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) (in /usr/local/bin/node)
  by 0xE324B6: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (in /usr/local/bin/node)
  by 0x178EBF8: Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit (in /usr/local/bin/node)
  by 0x1712B4F: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)

So apparently bind cannot be used within connect() as shown above, and documented, but this worked with the bundled libldap.a/liblber.a and system’s openssl 1.1.

Please adjust napi-ldap to be able to use the above code with OpenLDAP 2.6 (und thus OpenSSL 3).

dilyanpalauzov commented 1 year ago

To demostrate the problem I have adjusted the code of napi-ldap at https://github.com/dilyanpalauzov/napi-ldap.git.

When I call

$ git clone https://github.com/dilyanpalauzov/napi-ldap.git
$ cd napi-ldap
$ yarn
$ valgrind --tool=helgrind node a.js

The output is below. That is, cnx_bind() calls ldap_simple_bind(). It calls ldap_send_initial_request() and acquires a lock. Then ldap_simple_bind() exectutes (the callback) on_connect(), which calls cnx_bind(), which calls ldap_simple_bind() again, which again calls ldap_send_initial_request() and it tries to re-lock the mutex, on the same stack, it has already acquired.

I lack understanding of both Node API and LDAP API.

How shall I proceed?

==1795201== Helgrind, a thread error detector
==1795201== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==1795201== Using Valgrind-3.22.0.GIT and LibVEX; rerun with -h for copyright info
==1795201== Command: node a.js
==1795201== 
cnx.c:cnx_ctor() LAST
cnx_bind 1
cnx.c:cnx_bind() 2 next is ldap_simple_bind
cnx.c:on_connect() 1
init connect 1
cnx_bind 1
cnx.c:cnx_bind() 2 next is ldap_simple_bind
==1795201== ---Thread-Announcement------------------------------------------
==1795201== 
==1795201== Thread #1 is the program's root thread
==1795201== 
==1795201== ----------------------------------------------------------------
==1795201== 
==1795201== Thread #1: Attempt to re-lock a non-recursive lock I already hold
==1795201==    at 0x484896C: mutex_lock_WRK (hg_intercepts.c:934)
==1795201==    by 0x484DAA5: pthread_mutex_lock (hg_intercepts.c:960)
==1795201==    by 0x2A3679AE: ldap_send_initial_request (request.c:128)
==1795201==    by 0x2A35EF39: ldap_sasl_bind (sasl.c:164)
==1795201==    by 0x2A35EF39: ldap_sasl_bind (sasl.c:136)
==1795201==    by 0x2A35F674: ldap_simple_bind (sbind.c:81)
==1795201==    by 0xA2DA3DC: cnx_bind (in /git/napi-ldap/build/Release/napi_ldap.node)
==1795201==    by 0x9507E8: v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) (in /usr/local/bin/node)
==1795201==    by 0x10959AE: v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) (in /usr/local/bin/node)
==1795201==    by 0x1095EDD: v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, unsigned long*, int) (in /usr/local/bin/node)
==1795201==    by 0x10966D7: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (in /usr/local/bin/node)
==1795201==    by 0x1A63DF5: Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D40DB: Builtins_JSEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D3E02: Builtins_JSEntry (in /usr/local/bin/node)
==1795201==    by 0x119A465: v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) (in /usr/local/bin/node)
==1795201==    by 0x119B514: v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) (in /usr/local/bin/node)
==1795201==    by 0x104F768: v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) (in /usr/local/bin/node)
==1795201==    by 0x8D92EE: node::InternalMakeCallback(node::Environment*, v8::Local<v8::Object>, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context) (in /usr/local/bin/node)
==1795201==    by 0x968643: napi_make_callback (in /usr/local/bin/node)
==1795201==    by 0xA2DC5D0: on_connect (in /git/napi-ldap/build/Release/napi_ldap.node)
==1795201==    by 0x2A368912: ldap_int_connect_cbs (os-ip.c:546)
==1795201==    by 0x2A368C51: ldap_connect_to_host (os-ip.c:750)
==1795201==    by 0x2A3580E5: ldap_int_open_connection (open.c:441)
==1795201==    by 0x2A366E6C: ldap_new_connection (request.c:491)
==1795201==    by 0x2A357719: ldap_open_defconn (open.c:42)
==1795201==    by 0x2A367A47: ldap_send_initial_request (request.c:131)
==1795201==    by 0x2A35EF39: ldap_sasl_bind (sasl.c:164)
==1795201==    by 0x2A35EF39: ldap_sasl_bind (sasl.c:136)
==1795201==    by 0x2A35F674: ldap_simple_bind (sbind.c:81)
==1795201==  Lock was previously acquired
==1795201==    at 0x4848A43: mutex_lock_WRK (hg_intercepts.c:944)
==1795201==    by 0x484DAA5: pthread_mutex_lock (hg_intercepts.c:960)
==1795201==    by 0x2A3679AE: ldap_send_initial_request (request.c:128)
==1795201==    by 0x2A35EF39: ldap_sasl_bind (sasl.c:164)
==1795201==    by 0x2A35EF39: ldap_sasl_bind (sasl.c:136)
==1795201==    by 0x2A35F674: ldap_simple_bind (sbind.c:81)
==1795201==    by 0xA2DA3DC: cnx_bind (in /git/napi-ldap/build/Release/napi_ldap.node)
==1795201==    by 0x9507E8: v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) (in /usr/local/bin/node)
==1795201==    by 0x10959AE: v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) (in /usr/local/bin/node)
==1795201==    by 0x1095EDD: v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, unsigned long*, int) (in /usr/local/bin/node)
==1795201==    by 0x10966D7: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (in /usr/local/bin/node)
==1795201==    by 0x1A63DF5: Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D3401: Builtins_JSConstructStubGeneric (in /usr/local/bin/node)
==1795201==    by 0x1B1FC01: Builtins_ConstructHandler (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D5D1B: Builtins_InterpreterEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D40DB: Builtins_JSEntryTrampoline (in /usr/local/bin/node)
==1795201==    by 0x19D3E02: Builtins_JSEntry (in /usr/local/bin/node)
==1795201==    by 0x119A465: v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) (in /usr/local/bin/node)
==1795201==    by 0x119B514: v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) (in /usr/local/bin/node)
==1795201==    by 0x104F768: v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) (in /usr/local/bin/node)
==1795201==    by 0xFAADAF: node::builtins::BuiltinLoader::CompileAndCall(v8::Local<v8::Context>, char const*, node::Realm*) [clone .constprop.0] (in /usr/local/bin/node)
==1795201==    by 0x95A0C3: node::StartExecution(node::Environment*, char const*) (in /usr/local/bin/node)
==1795201==    by 0x95FD13: node::StartExecution(node::Environment*, std::function<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const&)>) (in /usr/local/bin/node)
==1795201==    by 0x9E2A6B: node::NodeMainInstance::Run() (in /usr/local/bin/node)