[gnutls-devel] [PATCH, RFC] Fix memory leak in wrap_nettle_hash_algorithm().

Lennert Buytenhek buytenh at wantstofly.org
Mon Oct 12 14:24:16 CEST 2015


wrap_nettle_hash_algorithm() leaks an mpz_t if it is called with
pk == GNUTLS_PK_RSA and sig == NULL, in which case it will return
without going through the regular exit path that clears the mpz_t
it allocated at the beginning of the function.  Use the regular
exit path instead to fix this.

This leak can be triggered via calls to
gnutls_pubkey_get_preferred_hash_algorithm().

Signed-off-by: Lennert Buytenhek <buytenh at wantstofly.org>

--- gnutls-3.3.18/lib/nettle/pk.c.orig	2015-05-23 11:36:13.000000000 +0300
+++ gnutls-3.3.18/lib/nettle/pk.c	2015-10-12 14:47:54.437991944 +0300
@@ -1963,41 +1963,42 @@ static int wrap_nettle_hash_algorithm(gn
 	struct rsa_public_key pub;
 	const mac_entry_st *me;
 	int ret;
 
 	mpz_init(s);
 
 	switch (pk) {
 	case GNUTLS_PK_DSA:
 	case GNUTLS_PK_EC:
 
 		me = _gnutls_dsa_q_to_hash(pk, issuer_params, NULL);
 		if (hash_algo)
 			*hash_algo = (gnutls_digest_algorithm_t)me->id;
 
 		ret = 0;
 		break;
 	case GNUTLS_PK_RSA:
 		if (sig == NULL) {	/* return a sensible algorithm */
 			if (hash_algo)
 				*hash_algo = GNUTLS_DIG_SHA256;
-			return 0;
+			ret = 0;
+			break;
 		}
 
 		_rsa_params_to_pubkey(issuer_params, &pub);
 
 		digest_size = sizeof(digest);
 
 		nettle_mpz_set_str_256_u(s, sig->size, sig->data);
 
 		ret = extract_digest_info(&pub, &di, &rdi, s);
 		if (ret == 0) {
 			ret = GNUTLS_E_PK_SIG_VERIFY_FAILED;
 			gnutls_assert();
 			goto cleanup;
 		}
 
 		digest_size = sizeof(digest);
 		if ((ret =
 		     decode_ber_digest_info(&di, hash_algo, digest,
 					    &digest_size)) < 0) {
 			gnutls_assert();


=== leaktest.c
#include <stdio.h>
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>

int main(void)
{
	gnutls_x509_privkey_t key;
	int ret;
	gnutls_privkey_t privkey;
	gnutls_pubkey_t pubkey;
	int i;

	gnutls_global_init();

	ret = gnutls_x509_privkey_init(&key);
	if (ret)
		goto err;

	ret = gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 512, 0);
	if (ret < 0)
		goto err;

	ret = gnutls_privkey_init(&privkey);
	if (ret < 0)
		goto err;

	ret = gnutls_privkey_import_x509(privkey, key, 0);
	if (ret < 0)
		goto err;

	ret = gnutls_pubkey_init(&pubkey);
	if (ret < 0)
		goto err;

	ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0);
	if (ret < 0)
		goto err;

	for (i = 0; i < 1000; i++) {
		gnutls_digest_algorithm_t dig;

		/*
		 * @@@ FIXME: this leaks memory on gnutls 3.3.18 (8 bytes
		 * per call on x86_64).
		 */
		ret = gnutls_pubkey_get_preferred_hash_algorithm(pubkey,
								 &dig, NULL);
		if (ret < 0)
			goto err;
	}

	gnutls_pubkey_deinit(pubkey);
	gnutls_privkey_deinit(privkey);
	gnutls_x509_privkey_deinit(key);

	gnutls_global_deinit();

	return 0;

err:
	gnutls_perror(ret);

	return 1;
}


=== valgrind output before patch
$ valgrind --leak-check=full ./leaktest 
==20025== Memcheck, a memory error detector
==20025== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==20025== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==20025== Command: ./leaktest
==20025== 
==20025== 
==20025== HEAP SUMMARY:
==20025==     in use at exit: 8,000 bytes in 1,000 blocks
==20025==   total heap usage: 2,928 allocs, 1,928 frees, 152,484 bytes allocated
==20025== 
==20025== 8,000 bytes in 1,000 blocks are definitely lost in loss record 1 of 1
==20025==    at 0x4C28C50: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20025==    by 0x604A7B8: __gmp_default_allocate (in /usr/lib64/libgmp.so.10.2.0)
==20025==    by 0x6059327: __gmpz_init (in /usr/lib64/libgmp.so.10.2.0)
==20025==    by 0x4F56AC7: wrap_nettle_hash_algorithm (pk.c:1967)
==20025==    by 0x4EEA51A: _gnutls_x509_verify_algorithm (verify.c:1201)
==20025==    by 0x4E84CA4: _gnutls_pk_get_hash_algorithm (gnutls_pk.c:233)
==20025==    by 0x4E9BC7C: gnutls_pubkey_get_preferred_hash_algorithm (gnutls_pubkey.c:270)
==20025==    by 0x40124E: main (in /home/buytenh/tlstest/leaktest)
==20025== 
==20025== LEAK SUMMARY:
==20025==    definitely lost: 8,000 bytes in 1,000 blocks
==20025==    indirectly lost: 0 bytes in 0 blocks
==20025==      possibly lost: 0 bytes in 0 blocks
==20025==    still reachable: 0 bytes in 0 blocks
==20025==         suppressed: 0 bytes in 0 blocks
==20025== 
==20025== For counts of detected and suppressed errors, rerun with: -v
==20025== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)


=== valgrind output after patch
$ valgrind --leak-check=full ./leaktest 
==22747== Memcheck, a memory error detector
==22747== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22747== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==22747== Command: ./leaktest
==22747== 
==22747== 
==22747== HEAP SUMMARY:
==22747==     in use at exit: 0 bytes in 0 blocks
==22747==   total heap usage: 2,750 allocs, 2,750 frees, 148,677 bytes allocated
==22747== 
==22747== All heap blocks were freed -- no leaks are possible
==22747== 
==22747== For counts of detected and suppressed errors, rerun with: -v
==22747== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)



More information about the Gnutls-devel mailing list