Memory leak in _gnutls_mpi_dprint_lz (possibly _gnutls_mpi_dprint)
Sam Varshavchik
mrsam at courier-mta.com
Mon Jun 23 02:25:44 CEST 2008
I'm chasing a complaint from valgrind that I'm leaking memory.
Here's valgrind's complaint:
==26738== 257 bytes in 1 blocks are definitely lost in loss record 2 of 4
==26738== at 0x4A0739E: malloc (vg_replace_malloc.c:207)
==26738== by 0x35068328F6: _gnutls_mpi_dprint_lz (gnutls_mpi.c:146)
==26738== by 0x350683E47C: _gnutls_dh_set_peer_public (gnutls_state.c:474)
==26738== by 0x3506843819: _gnutls_proc_dh_common_server_kx (auth_dh_common.c:297)
==26738== by 0x350683BB4F: proc_dhe_server_kx (auth_dhe.c:199)
==26738== by 0x350682AF81: _gnutls_recv_server_kx_message (gnutls_kx.c:339)
==26738== by 0x35068273DF: _gnutls_handshake_client (gnutls_handshake.c:2311)
==26738== by 0x3506827F77: gnutls_handshake (gnutls_handshake.c:2193)
Here's what I've been able to figure out. I'm running gnutls 2.0.4, but I
checked 2.4.0, and the affected bits have not changed, the following should
still be applicable.
_gnutls_mpi_dprint_lz() allocates a buffer:
if (bytes != 0)
buf = gnutls_malloc (bytes);
. . . and puts it into its gnutls_datum_t parameter:
if (!ret)
{
dest->data = buf;
dest->size = bytes;
return 0;
}
If I set a breakpoint in _gnutls_dh_set_peer_public(), then I find that this
breakpoint gets hit twice from gnutls_handshake():
#0 _gnutls_dh_set_peer_public (session=0x144fcc0, public=0x1431a70)
at gnutls_state.c:474
#1 0x000000350684381a in _gnutls_proc_dh_common_server_kx (
session=<value optimized out>, data=<value optimized out>,
_data_size=<value optimized out>, psk=<value optimized out>)
at auth_dh_common.c:297
#2 0x000000350683bb50 in proc_dhe_server_kx (session=<value optimized out>,
data=<value optimized out>, _data_size=<value optimized out>)
at auth_dhe.c:199
#3 0x000000350682af82 in _gnutls_recv_server_kx_message (
session=<value optimized out>) at gnutls_kx.c:339
#4 0x00000035068273e0 in _gnutls_handshake_client (
session=<value optimized out>) at gnutls_handshake.c:2311
#5 0x0000003506827f78 in gnutls_handshake (session=<value optimized out>)
at gnutls_handshake.c:2193
Second breakpoint hit:
#0 _gnutls_dh_set_peer_public (session=0x144fcc0, public=0x1431a70)
at gnutls_state.c:474
#1 0x0000003506843b8f in _gnutls_gen_dh_common_client_kx (
session=<value optimized out>, data=<value optimized out>)
at auth_dh_common.c:167
#2 0x000000350682aac1 in _gnutls_send_client_kx_message (
session=<value optimized out>, again=<value optimized out>)
at gnutls_kx.c:231
#3 0x000000350682736d in _gnutls_handshake_client (
session=<value optimized out>) at gnutls_handshake.c:2352
#4 0x0000003506827f78 in gnutls_handshake (session=<value optimized out>)
at gnutls_handshake.c:2193
Observe that _gnutls_dh_set_peer_public() invokes _gnutls_mpi_dprint_lz(),
passing it the address of _gnutls_get_auth_info(session)->dh.public_key.
I hit the breakpoint the first time:
(gdb) p dh
$20 = (dh_info_st *) 0x8da8f8
(gdb) p $20->public_key
$21 = {data = 0x0, size = 0}
Empty unallocated buffer. Let me step into and out of the
_gnutls_mpi_dprint_lz() function call:
(gdb) s
_gnutls_mpi_dprint_lz (dest=<value optimized out>, a=<value optimized out>)
at gnutls_mpi.c:135
135 {
(gdb) finish
Run till exit from #0 _gnutls_mpi_dprint_lz (dest=<value optimized out>,
a=<value optimized out>) at gnutls_mpi.c:135
_gnutls_dh_set_peer_public (session=0x8d9bd0, public=0x8ad2b0)
at gnutls_state.c:475
475 if (ret < 0)
Value returned is $22 = 0
(gdb) p $20->public_key
$23 = {data = 0x8e2cd0 "", size = 257}
_gnutls_mpi_dprint_lz() does its thing, and returns, leaving the public_key
datum allocated. Fine.
I'm going to let it run now, and let it hit the breakpoint the 2nd time:
(gdb) c
Continuing.
Breakpoint 3, _gnutls_dh_set_peer_public (session=0x8d9bd0, public=0x8ad2b0)
at gnutls_state.c:474
474 ret = _gnutls_mpi_dprint_lz (&dh→public_key, public);
(gdb) p dh
$24 = (dh_info_st *) 0x8da8f8
(gdb) p $24->public_key
$25 = {data = 0x8e2cd0 "", size = 257}
The buffer is still there, where it was before. Now you can see what's going
to happen:
(gdb) s
_gnutls_mpi_dprint_lz (dest=<value optimized out>, a=<value optimized out>)
at gnutls_mpi.c:135
135 {
(gdb) finish
Run till exit from #0 _gnutls_mpi_dprint_lz (dest=<value optimized out>,
a=<value optimized out>) at gnutls_mpi.c:135
_gnutls_dh_set_peer_public (session=0x8d9bd0, public=0x8ad2b0)
at gnutls_state.c:475
475 if (ret < 0)
Value returned is $26 = 0
(gdb) p $24->public_key
$27 = {data = 0x8e7460 "", size = 257}
_gnutls_mpi_dprint_lz() did not check that its datum parameter was already
initialized with a memory chunk, and allocated another memory block,
overwriting the pointer, and leaking that memory block.
I cannot say what's the correct fix: have _gnutls_mpi_dprint_lz() clean up
its gnutls_datum_t parameter, if it already points to some other memory
chunk, or have _gnutls_dh_set_peer_public() check if dh->public_key is
already initialized, and avoid calling _gnutls_mpi_dprint_lz() if so.
I also see the same logic in _gnutls_mpi_dprint() as well, there's a good
possibility that there are execution paths that might trigger a leak there
as well.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: </pipermail/attachments/20080622/bb69f20e/attachment.pgp>
More information about the Gnutls-devel
mailing list