On Mon, Jul 19, 2004 at 05:35:36PM +0200, Matthias Urlichs wrote: > Looks like a nicely impossible-to-find memory corruption bug. Happiness. > Did you try to reproduce it under Electric Fence? I have just reproduced the bug under Electric Fence. The SEGV occurs slightly earlier: Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 16386 (LWP 15079)] 0x402924fe in _gcry_mpi_copy (a=0x85cfbff6) at mpiutil.c:216 216 if( a && (a->flags & 4) ) { (gdb) bt #0 0x402924fe in _gcry_mpi_copy (a=0x85cfbff6) at mpiutil.c:216 #1 0x40292a48 in gcry_mpi_copy (a=0x85cfbff6) at mpiutil.c:343 #2 0x401fe3fc in _gnutls_get_dh_params (dh_primes=0x6c107ff8, ret_p=0xbf7fc248, ret_g=0xbf7fc24c) at gnutls_dh_primes.c:45 #3 0x401fe292 in proc_dhe_client_kx (session=0x6c01c630, data=0x85cfbff6
, _data_size=2244984822) at auth_dhe.c:268 #4 0x401ee548 in _gnutls_recv_client_kx_message (session=0x6c01c630) at gnutls_kx.c:329 #5 0x401e94c0 in _gnutls_handshake_server (session=0x6c01c630) at gnutls_handshake.c:2241 #6 0x401e8ad6 in gnutls_handshake (session=0x6c01c630) at gnutls_handshake.c:1892 #7 0x400569a7 in SSL_do_handshake (ssl=0x485d0fdc, end=GNUTLS_SERVER) at gnutls.c:627 #8 0x40056acd in gnutls_SSL_accept (ssl=0x485d0fdc) at gnutls.c:670 #9 0x40054394 in ldap_pvt_tls_accept (sb=0x6bc15fe4, ctx_arg=0x0) at tls.c:928 #10 0x08058ff0 in connection_read (s=32) at /root/tls/openldap2-2.1.30/servers/slapd/connection.c:1134 #11 0x080564ab in slapd_daemon_task (ptr=0x0) at /root/tls/openldap2-2.1.30/servers/slapd/daemon.c:1863 #12 0x40312e51 in pthread_start_thread () from /lib/libpthread.so.0 #13 0x40312ecf in pthread_start_thread_event () from /lib/libpthread.so.0 #14 0x4044969a in clone () from /lib/libc.so.6 That a->d pointed to 0x10 in a previous post doesn't seem to be the root of failure, but rather a consequence of this: (gdb) f 0 #0 0x402924fe in _gcry_mpi_copy (a=0x85cfbff6) at mpiutil.c:216 216 if( a && (a->flags & 4) ) { (gdb) p *a Cannot access memory at address 0x85cfbff6 (gdb) The variable 'a' is the same as cred->dh_params in #3: (gdb) f 3 #3 0x401fe292 in proc_dhe_client_kx (session=0x6c01c630, data=0x85cfbff6
, _data_size=2244984822) at auth_dhe.c:268 268 if ( (ret=_gnutls_get_dh_params( cred->dh_params, &p, &g)) < 0) { (gdb) p cred->dh_params Cannot access memory at address 0x85cfbff6 (gdb) Before, cred gets assigned the following: auth_dhe.c: cred = _gnutls_get_cred(session->key, GNUTLS_CRD_CERTIFICATE, NULL); const void *_gnutls_get_cred( GNUTLS_KEY key, gnutls_credentials_type type, int *err) { const void *retval = NULL; int _err = -1; AUTH_CRED * ccred; if (key == NULL) goto out; ccred = key->cred; while(ccred!=NULL) { if (ccred->algorithm==type) { break; } ccred = ccred->next; } if (ccred==NULL) goto out; _err = 0; retval = ccred->credentials; out: if (err!=NULL) *err=_err; return retval; } _gnutls_get_cred walks through the linked list of session->key->cred and returns the first element whose algorithm matches GNUTLS_CRD_CERT.. Here, session->key->cred consists of only one element (next points to NULL) that also matches the algorithm, and thus processing should stop there, and session->key->cred->credentials be returned. (gdb) p *(session->key->cred) $42 = {algorithm = GNUTLS_CRD_CERTIFICATE, credentials = 0x47210fb8, next = 0x0} Consequently, session->key->cred->credentials and cred should be *identical*. But they aren't (0x47210fb8 != 0x85cfbff6): (gdb) p session->key->cred->credentials $53 = (void *) 0x47210fb8 (gdb) p cred $54 = (gdb) p *cred Cannot access memory at address 0x85cfbff6 I'm assuming that when cred is assigned, they are still identical. But when does cred get changed? I have no idea. static int proc_dhe_client_kx(gnutls_session session, opaque * data, size_t _data_size) { int bits; const gnutls_certificate_credentials cred; int ret; GNUTLS_MPI p, g; bits = _gnutls_dh_get_prime_bits( session); cred = _gnutls_get_cred(session->key, GNUTLS_CRD_CERTIFICATE, NULL); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if ( (ret=_gnutls_get_dh_params( cred->dh_params, &p, &g)) < 0) { gnutls_assert(); return ret; } ret = _gnutls_proc_dh_common_client_kx( session, data, _data_size, g, p); _gnutls_mpi_release(&g); _gnutls_mpi_release(&p); return ret; }