https://bugs.openldap.org/show_bug.cgi?id=9952
Issue ID: 9952 Summary: Crash on exit with OpenSSL 3 Product: OpenLDAP Version: 2.6.2 Hardware: All OS: Linux Status: UNCONFIRMED Keywords: needs_review Severity: normal Priority: --- Component: libraries Assignee: bugs@openldap.org Reporter: artur.zaprzala@gmail.com Target Milestone: ---
A program using libldap will crash on exit after using SSL connection.
How to reproduce on CentOS 9: Uncomment the following lines in /etc/pki/tls/openssl.cnf: [provider_sect] legacy = legacy_sect [legacy_sect] activate = 1
Run the command (you must enter a valid LDAP server address): python3 -c "import ldap; ldap.initialize('ldaps://<LDAP SERVER ADDRESS>').whoami_s()"
Another example (no server required): python3 -c "import ctypes; ctypes.CDLL('libldap.so.2').ldap_pvt_tls_init_def_ctx(0)"
Results: Segmentation fault (core dumped)
Backtrace from gdb: Program received signal SIGSEGV, Segmentation fault. 0 ___pthread_rwlock_rdlock (rwlock=0x0) at pthread_rwlock_rdlock.c:27 1 0x00007ffff7c92f3d in CRYPTO_THREAD_read_lock (lock=<optimized out>) at crypto/threads_pthread.c:85 2 0x00007ffff7c8b126 in ossl_lib_ctx_get_data (ctx=0x7ffff7eff540 <default_context_int.lto_priv>, index=1, meth=0x7ffff7eb8a00 <provider_store_method.lto_priv>) at crypto/context.c:398 3 0x00007ffff7c98bea in get_provider_store (libctx=<optimized out>) at crypto/provider_core.c:334 4 ossl_provider_deregister_child_cb (handle=0x5555555ed620) at crypto/provider_core.c:1752 5 0x00007ffff7c8bf2f in ossl_provider_deinit_child (ctx=0x5555555d2650) at crypto/provider_child.c:279 6 OSSL_LIB_CTX_free (ctx=0x5555555d2650) at crypto/context.c:283 7 OSSL_LIB_CTX_free (ctx=0x5555555d2650) at crypto/context.c:276 8 0x00007ffff7634af6 in legacy_teardown (provctx=0x5555555ee9f0) at providers/legacyprov.c:168 9 0x00007ffff7c9901b in ossl_provider_teardown (prov=0x5555555ed620) at crypto/provider_core.c:1477 10 ossl_provider_free (prov=0x5555555ed620) at crypto/provider_core.c:683 11 0x00007ffff7c63956 in ossl_provider_free (prov=<optimized out>) at crypto/provider_core.c:668 12 evp_cipher_free_int (cipher=0x555555916c10) at crypto/evp/evp_enc.c:1632 13 EVP_CIPHER_free (cipher=0x555555916c10) at crypto/evp/evp_enc.c:1647 14 0x00007ffff7a6bc1d in ssl_evp_cipher_free (cipher=0x555555916c10) at ssl/ssl_lib.c:5925 15 ssl_evp_cipher_free (cipher=0x555555916c10) at ssl/ssl_lib.c:5915 16 SSL_CTX_free (a=0x555555ec1020) at ssl/ssl_lib.c:3455 17 SSL_CTX_free (a=0x555555ec1020) at ssl/ssl_lib.c:3392 18 0x00007fffe95edb89 in ldap_int_tls_destroy (lo=0x7fffe9616000 <ldap_int_global_options>) at /usr/src/debug/openldap-2.6.2-1.el9_0.x86_64/openldap-2.6.2/libraries/libldap/tls2.c:104 19 0x00007ffff7fd100b in _dl_fini () at dl-fini.c:138 20 0x00007ffff7873475 in __run_exit_handlers (status=0, listp=0x7ffff7a11658 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:113 21 0x00007ffff78735f0 in __GI_exit (status=<optimized out>) at exit.c:143 22 0x00007ffff785be57 in __libc_start_call_main (main=main@entry=0x55555556aa20 <main>, argc=argc@entry=4, argv=argv@entry=0x7fffffffe2b8) at ../sysdeps/nptl/libc_start_call_main.h:74 23 0x00007ffff785befc in __libc_start_main_impl (main=0x55555556aa20 <main>, argc=4, argv=0x7fffffffe2b8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe2a8) at ../csu/libc-start.c:409 24 0x000055555556b575 in _start ()
The problem is that ldap_int_tls_destroy() is called after the clean up of libssl.
On program exit, at first default_context_int is cleaned up (OPENSSL_cleanup() was registered with atexit()): 0 ossl_lib_ctx_default_deinit () at crypto/context.c:196 1 OPENSSL_cleanup () at crypto/init.c:424 2 OPENSSL_cleanup () at crypto/init.c:338 3 0x00007ffff7873475 in __run_exit_handlers (status=0, listp=0x7ffff7a11658 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:113 4 0x00007ffff78735f0 in __GI_exit (status=<optimized out>) at exit.c:143 5 0x00007ffff785be57 in __libc_start_call_main (main=main@entry=0x55555556aa20 <main>, argc=argc@entry=4, argv=argv@entry=0x7fffffffe2c8) at ../sysdeps/nptl/libc_start_call_main.h:74 6 0x00007ffff785befc in __libc_start_main_impl (main=0x55555556aa20 <main>, argc=4, argv=0x7fffffffe2c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe2b8) at ../csu/libc-start.c:409 7 0x000055555556b575 in _start ()
Then ossl_lib_ctx_get_data() tries to use default_context_int.lock, which is NULL. ldap_int_tls_destroy() is called by ldap_int_destroy_global_options(), registered by "__attribute__ ((destructor))".
It seems that shared library destructors are always called before functions registered with atexit(). A solution may be to modify libraries/libldap/init.c to use atexit() instead of "__attribute__ ((destructor))". atexit() manual page says: "Since glibc 2.2.3, atexit() can be used within a shared library to establish functions that are called when the shared library is unloaded.". Functions registered with atexit() are called in the reverse order of their registration, so libssl must by initialized before libldap. If the order is wrong, libldap should detect it somehow and exit with abort().