CVE-2016-5769: Integer Overflow or Wraparound (CWE-190)
value: Multiple integer overflows in mcrypt.c in the mcrypt extension in PHP before 5.5.37, 5.6.x before 5.6.23, and 7.x before 7.0.8 allow remote attackers to cause a denial of service (heap-based buffer overflow and application crash) or possibly have unspecified other impact via a crafted length value, related to the (1) mcrypt_generic and (2) mdecrypt_generic functions.
CWE description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
CVSS v3 base score: 9.8
Published: 2016-08-07T10:59Z
Repository: https://github.com/php/php-src
Language: C
Files changed: ext/mcrypt/mcrypt.c
Commit date: 2016-06-20 21:51:42 -0700
Commit message: Fix bug #72455:  Heap Overflow due to integer overflows

Diff: diff --git a/ext/mcrypt/mcrypt.c b/ext/mcrypt/mcrypt.c
index 194660d864e..3cbb913414a 100644
--- a/ext/mcrypt/mcrypt.c
+++ b/ext/mcrypt/mcrypt.c
@@ -42,11 +42,11 @@
 #include "ext/standard/php_rand.h"
 #include "php_mcrypt_filter.h"
 
 static int le_mcrypt;
 
-typedef struct _php_mcrypt { 
+typedef struct _php_mcrypt {
 	MCRYPT td;
 	zend_bool init;
 } php_mcrypt;
 
 /* {{{ arginfo */
@@ -290,11 +290,11 @@ static PHP_MSHUTDOWN_FUNCTION(mcrypt);
 
 ZEND_DECLARE_MODULE_GLOBALS(mcrypt)
 
 zend_module_entry mcrypt_module_entry = {
 	STANDARD_MODULE_HEADER,
-	"mcrypt", 
+	"mcrypt",
 	mcrypt_functions,
 	PHP_MINIT(mcrypt), PHP_MSHUTDOWN(mcrypt),
 	NULL, NULL,
 	PHP_MINFO(mcrypt),
 	NO_VERSION_YET,
@@ -374,11 +374,11 @@ ZEND_GET_MODULE(mcrypt)
 	zval *mcryptind;											\
 	php_mcrypt *pm;													\
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &mcryptind) == FAILURE) {			\
 		return;																\
 	}																						\
-	ZEND_FETCH_RESOURCE (pm, php_mcrypt *, &mcryptind, -1, "MCrypt", le_mcrypt);				
+	ZEND_FETCH_RESOURCE (pm, php_mcrypt *, &mcryptind, -1, "MCrypt", le_mcrypt);
 
 #define MCRYPT_GET_MODE_DIR_ARGS(DIRECTORY)								\
 	char *dir = NULL;                                                   \
 	int   dir_len;                                                      \
 	char *module;                                                       \
@@ -405,11 +405,11 @@ PHP_INI_BEGIN()
 PHP_INI_END()
 
 static void php_mcrypt_module_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
 {
 	php_mcrypt *pm = (php_mcrypt *) rsrc->ptr;
-	if (pm) {	
+	if (pm) {
 		mcrypt_generic_deinit(pm->td);
 		mcrypt_module_close(pm->td);
 		efree(pm);
 		pm = NULL;
 	}
@@ -546,11 +546,11 @@ PHP_MINFO_FUNCTION(mcrypt) /* {{{ */
 	php_info_print_table_row(2, "Supported ciphers", tmp1.c);
 	php_info_print_table_row(2, "Supported modes", tmp2.c);
 	smart_str_free(&tmp1);
 	smart_str_free(&tmp2);
 	php_info_print_table_end();
-	
+
 	DISPLAY_INI_ENTRIES();
 }
 /* }}} */
 
 /* {{{ proto resource mcrypt_module_open(string cipher, string cipher_directory, string mode, string mode_directory)
@@ -561,21 +561,21 @@ PHP_FUNCTION(mcrypt_module_open)
 	char *mode,   *mode_dir;
 	int   cipher_len, cipher_dir_len;
 	int   mode_len,   mode_dir_len;
 	MCRYPT td;
 	php_mcrypt *pm;
-   
+
 	if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, "ssss",
 		&cipher, &cipher_len, &cipher_dir, &cipher_dir_len,
 		&mode,   &mode_len,   &mode_dir,   &mode_dir_len)) {
 		return;
 	}
-	
+
 	td = mcrypt_module_open (
 		cipher,
 		cipher_dir_len > 0 ? cipher_dir : MCG(algorithms_dir),
-		mode, 
+		mode,
 		mode_dir_len > 0 ? mode_dir : MCG(modes_dir)
 	);
 
 	if (td == MCRYPT_FAILED) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not open encryption module");
@@ -598,11 +598,11 @@ PHP_FUNCTION(mcrypt_generic_init)
 	zval *mcryptind;
 	unsigned char *key_s, *iv_s;
 	int max_key_size, key_size, iv_size;
 	php_mcrypt *pm;
 	int result = 0;
-	
+
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &mcryptind, &key, &key_len, &iv, &iv_len) == FAILURE) {
 		return;
 	}
 
 	ZEND_FETCH_RESOURCE(pm, php_mcrypt *, &mcryptind, -1, "MCrypt", le_mcrypt);
@@ -677,11 +677,11 @@ PHP_FUNCTION(mcrypt_generic)
 	int block_size, data_size;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &mcryptind, &data, &data_len) == FAILURE) {
 		return;
 	}
-	
+
 	ZEND_FETCH_RESOURCE(pm, php_mcrypt *, &mcryptind, -1, "MCrypt", le_mcrypt);
 	PHP_MCRYPT_INIT_CHECK
 
 	if (data_len == 0) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "An empty string was passed");
@@ -690,20 +690,24 @@ PHP_FUNCTION(mcrypt_generic)
 
 	/* Check blocksize */
 	if (mcrypt_enc_is_block_mode(pm->td) == 1) { /* It's a block algorithm */
 		block_size = mcrypt_enc_get_block_size(pm->td);
 		data_size = (((data_len - 1) / block_size) + 1) * block_size;
+		if (data_size <= 0) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Integer overflow in data size");
+			RETURN_FALSE;
+		}
 		data_s = emalloc(data_size + 1);
 		memset(data_s, 0, data_size);
 		memcpy(data_s, data, data_len);
 	} else { /* It's not a block algorithm */
 		data_size = data_len;
 		data_s = emalloc(data_size + 1);
 		memset(data_s, 0, data_size);
 		memcpy(data_s, data, data_len);
 	}
-	
+
 	mcrypt_generic(pm->td, data_s, data_size);
 	data_s[data_size] = '\0';
 
 	RETVAL_STRINGL(data_s, data_size, 1);
 	efree(data_s);
@@ -718,15 +722,15 @@ PHP_FUNCTION(mdecrypt_generic)
 	char *data;
 	int data_len;
 	php_mcrypt *pm;
 	char* data_s;
 	int block_size, data_size;
-	
+
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &mcryptind, &data, &data_len) == FAILURE) {
 		return;
 	}
-	
+
 	ZEND_FETCH_RESOURCE(pm, php_mcrypt * , &mcryptind, -1, "MCrypt", le_mcrypt);
 	PHP_MCRYPT_INIT_CHECK
 
 	if (data_len == 0) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "An empty string was passed");
@@ -735,20 +739,24 @@ PHP_FUNCTION(mdecrypt_generic)
 
 	/* Check blocksize */
 	if (mcrypt_enc_is_block_mode(pm->td) == 1) { /* It's a block algorithm */
 		block_size = mcrypt_enc_get_block_size(pm->td);
 		data_size = (((data_len - 1) / block_size) + 1) * block_size;
+		if (data_size <= 0) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Integer overflow in data size");
+			RETURN_FALSE;
+		}
 		data_s = emalloc(data_size + 1);
 		memset(data_s, 0, data_size);
 		memcpy(data_s, data, data_len);
 	} else { /* It's not a block algorithm */
 		data_size = data_len;
 		data_s = emalloc(data_size + 1);
 		memset(data_s, 0, data_size);
 		memcpy(data_s, data, data_len);
 	}
-	
+
 	mdecrypt_generic(pm->td, data_s, data_size);
 
 	RETVAL_STRINGL(data_s, data_size, 1);
 	efree(data_s);
 }
@@ -758,11 +766,11 @@ PHP_FUNCTION(mdecrypt_generic)
    This function decrypts the crypttext */
 PHP_FUNCTION(mcrypt_enc_get_supported_key_sizes)
 {
 	int i, count = 0;
 	int *key_sizes;
-	
+
 	MCRYPT_GET_TD_ARG
 	array_init(return_value);
 
 	key_sizes = mcrypt_enc_get_supported_key_sizes(pm->td, &count);
 
@@ -827,11 +835,11 @@ PHP_FUNCTION(mcrypt_enc_is_block_algorithm_mode)
 PHP_FUNCTION(mcrypt_enc_is_block_algorithm)
 {
 	MCRYPT_GET_TD_ARG
 
 	if (mcrypt_enc_is_block_algorithm(pm->td) == 1) {
-		RETURN_TRUE 
+		RETURN_TRUE
 	} else {
 		RETURN_FALSE
 	}
 }
 /* }}} */
@@ -906,11 +914,11 @@ PHP_FUNCTION(mcrypt_enc_get_modes_name)
 /* {{{ proto bool mcrypt_module_self_test(string algorithm [, string lib_dir])
    Does a self test of the module "module" */
 PHP_FUNCTION(mcrypt_module_self_test)
 {
 	MCRYPT_GET_MODE_DIR_ARGS(algorithms_dir);
-	
+
 	if (mcrypt_module_self_test(module, dir) == 0) {
 		RETURN_TRUE;
 	} else {
 		RETURN_FALSE;
 	}
@@ -920,11 +928,11 @@ PHP_FUNCTION(mcrypt_module_self_test)
 /* {{{ proto bool mcrypt_module_is_block_algorithm_mode(string mode [, string lib_dir])
    Returns TRUE if the mode is for use with block algorithms */
 PHP_FUNCTION(mcrypt_module_is_block_algorithm_mode)
 {
 	MCRYPT_GET_MODE_DIR_ARGS(modes_dir)
-	
+
 	if (mcrypt_module_is_block_algorithm_mode(module, dir) == 1) {
 		RETURN_TRUE;
 	} else {
 		RETURN_FALSE;
 	}
@@ -934,11 +942,11 @@ PHP_FUNCTION(mcrypt_module_is_block_algorithm_mode)
 /* {{{ proto bool mcrypt_module_is_block_algorithm(string algorithm [, string lib_dir])
    Returns TRUE if the algorithm is a block algorithm */
 PHP_FUNCTION(mcrypt_module_is_block_algorithm)
 {
 	MCRYPT_GET_MODE_DIR_ARGS(algorithms_dir)
-	
+
 	if (mcrypt_module_is_block_algorithm(module, dir) == 1) {
 		RETURN_TRUE;
 	} else {
 		RETURN_FALSE;
 	}
@@ -948,11 +956,11 @@ PHP_FUNCTION(mcrypt_module_is_block_algorithm)
 /* {{{ proto bool mcrypt_module_is_block_mode(string mode [, string lib_dir])
    Returns TRUE if the mode outputs blocks of bytes */
 PHP_FUNCTION(mcrypt_module_is_block_mode)
 {
 	MCRYPT_GET_MODE_DIR_ARGS(modes_dir)
-	
+
 	if (mcrypt_module_is_block_mode(module, dir) == 1) {
 		RETURN_TRUE;
 	} else {
 		RETURN_FALSE;
 	}
@@ -962,32 +970,32 @@ PHP_FUNCTION(mcrypt_module_is_block_mode)
 /* {{{ proto int mcrypt_module_get_algo_block_size(string algorithm [, string lib_dir])
    Returns the block size of the algorithm */
 PHP_FUNCTION(mcrypt_module_get_algo_block_size)
 {
 	MCRYPT_GET_MODE_DIR_ARGS(algorithms_dir)
-	
+
 	RETURN_LONG(mcrypt_module_get_algo_block_size(module, dir));
 }
 /* }}} */
 
 /* {{{ proto int mcrypt_module_get_algo_key_size(string algorithm [, string lib_dir])
    Returns the maximum supported key size of the algorithm */
 PHP_FUNCTION(mcrypt_module_get_algo_key_size)
 {
 	MCRYPT_GET_MODE_DIR_ARGS(algorithms_dir);
-	
+
 	RETURN_LONG(mcrypt_module_get_algo_key_size(module, dir));
 }
 /* }}} */
 
 /* {{{ proto array mcrypt_module_get_supported_key_sizes(string algorithm [, string lib_dir])
    This function decrypts the crypttext */
 PHP_FUNCTION(mcrypt_module_get_supported_key_sizes)
 {
 	int i, count = 0;
 	int *key_sizes;
-	
+
 	MCRYPT_GET_MODE_DIR_ARGS(algorithms_dir)
 	array_init(return_value);
 
 	key_sizes = mcrypt_module_get_algo_supported_key_sizes(module, dir, &count);
 
@@ -1009,11 +1017,11 @@ PHP_FUNCTION(mcrypt_list_algorithms)
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s",
 		&lib_dir, &lib_dir_len) == FAILURE) {
 		return;
 	}
-	
+
 	array_init(return_value);
 	modules = mcrypt_list_algorithms(lib_dir, &count);
 
 	if (count == 0) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No algorithms found in module dir");
@@ -1056,22 +1064,22 @@ PHP_FUNCTION(mcrypt_list_modes)
    Get the key size of cipher */
 PHP_FUNCTION(mcrypt_get_key_size)
 {
 	char *cipher;
 	char *module;
-	int   cipher_len, module_len; 
+	int   cipher_len, module_len;
 	char *cipher_dir_string;
 	char *module_dir_string;
 	MCRYPT td;
 
 	MCRYPT_GET_INI
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
 		&cipher, &cipher_len, &module, &module_len) == FAILURE) {
 		return;
 	}
-	
+
 	td = mcrypt_module_open(cipher, cipher_dir_string, module, module_dir_string);
 	if (td != MCRYPT_FAILED) {
 		RETVAL_LONG(mcrypt_enc_get_key_size(td));
 		mcrypt_module_close(td);
 	} else {
@@ -1085,22 +1093,22 @@ PHP_FUNCTION(mcrypt_get_key_size)
    Get the key size of cipher */
 PHP_FUNCTION(mcrypt_get_block_size)
 {
 	char *cipher;
 	char *module;
-	int   cipher_len, module_len; 
+	int   cipher_len, module_len;
 	char *cipher_dir_string;
 	char *module_dir_string;
 	MCRYPT td;
 
 	MCRYPT_GET_INI
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
 		&cipher, &cipher_len, &module, &module_len) == FAILURE) {
 		return;
 	}
-	
+
 	td = mcrypt_module_open(cipher, cipher_dir_string, module, module_dir_string);
 	if (td != MCRYPT_FAILED) {
 		RETVAL_LONG(mcrypt_enc_get_block_size(td));
 		mcrypt_module_close(td);
 	} else {
@@ -1114,22 +1122,22 @@ PHP_FUNCTION(mcrypt_get_block_size)
    Get the IV size of cipher (Usually the same as the blocksize) */
 PHP_FUNCTION(mcrypt_get_iv_size)
 {
 	char *cipher;
 	char *module;
-	int   cipher_len, module_len; 
+	int   cipher_len, module_len;
 	char *cipher_dir_string;
 	char *module_dir_string;
 	MCRYPT td;
 
 	MCRYPT_GET_INI
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
 		&cipher, &cipher_len, &module, &module_len) == FAILURE) {
 		return;
 	}
-	
+
 	td = mcrypt_module_open(cipher, cipher_dir_string, module, module_dir_string);
 	if (td != MCRYPT_FAILED) {
 		RETVAL_LONG(mcrypt_enc_get_iv_size(td));
 		mcrypt_module_close(td);
 	} else {
@@ -1215,26 +1223,26 @@ static void php_mcrypt_do_crypt(char* cipher, const char *key, int key_len, cons
 		memcpy(key_s, key, MIN(key_len, key_length_sizes[0]));
 		use_key_length = key_length_sizes[0];
 	} else { /* dertermine smallest supported key > length of requested key */
 		use_key_length = max_key_length; /* start with max key length */
 		for (i = 0; i < count; i++) {
-			if (key_length_sizes[i] >= key_len && 
+			if (key_length_sizes[i] >= key_len &&
 				key_length_sizes[i] < use_key_length)
 			{
 				use_key_length = key_length_sizes[i];
 			}
 		}
 		key_s = emalloc(use_key_length);
 		memset(key_s, 0, use_key_length);
 		memcpy(key_s, key, MIN(key_len, use_key_length));
 	}
 	mcrypt_free (key_length_sizes);
-	
+
 	/* Check IV */
 	iv_s = NULL;
 	iv_size = mcrypt_enc_get_iv_size (td);
-	
+
 	/* IV is required */
 	if (mcrypt_enc_mode_has_iv(td) == 1) {
 		if (argc == 5) {
 			if (iv_size != iv_len) {
 				php_error_docref(NULL TSRMLS_CC, E_WARNING, MCRYPT_IV_WRONG_SIZE);
@@ -1270,11 +1278,11 @@ static void php_mcrypt_do_crypt(char* cipher, const char *key, int key_len, cons
 	if (dencrypt == MCRYPT_ENCRYPT) {
 		mcrypt_generic(td, data_s, data_size);
 	} else {
 		mdecrypt_generic(td, data_s, data_size);
 	}
-	
+
 	RETVAL_STRINGL(data_s, data_size, 1);
 
 	/* freeing vars */
 	mcrypt_generic_end(td);
 	if (key_s != NULL) {
@@ -1292,13 +1300,13 @@ static void php_mcrypt_do_crypt(char* cipher, const char *key, int key_len, cons
 PHP_FUNCTION(mcrypt_encrypt)
 {
 	zval **mode;
 	char *cipher, *key, *data, *iv = NULL;
 	int cipher_len, key_len, data_len, iv_len = 0;
-	
+
 	MCRYPT_GET_CRYPT_ARGS
-	
+
 	convert_to_string_ex(mode);
 
 	php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, Z_STRVAL_PP(mode), iv, iv_len, ZEND_NUM_ARGS(), MCRYPT_ENCRYPT, return_value TSRMLS_CC);
 }
 /* }}} */
@@ -1310,11 +1318,11 @@ PHP_FUNCTION(mcrypt_decrypt)
 	zval **mode;
 	char *cipher, *key, *data, *iv = NULL;
 	int cipher_len, key_len, data_len, iv_len = 0;
 
 	MCRYPT_GET_CRYPT_ARGS
-	
+
 	convert_to_string_ex(mode);
 
 	php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, Z_STRVAL_PP(mode), iv, iv_len, ZEND_NUM_ARGS(), MCRYPT_DECRYPT, return_value TSRMLS_CC);
 }
 /* }}} */
@@ -1324,11 +1332,11 @@ PHP_FUNCTION(mcrypt_decrypt)
 PHP_FUNCTION(mcrypt_ecb)
 {
 	zval **mode;
 	char *cipher, *key, *data, *iv = NULL;
 	int cipher_len, key_len, data_len, iv_len = 0;
-	
+
 	MCRYPT_GET_CRYPT_ARGS
 
 	convert_to_long_ex(mode);
 
 	php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "ecb", iv, iv_len, ZEND_NUM_ARGS(), Z_LVAL_PP(mode), return_value TSRMLS_CC);
@@ -1356,11 +1364,11 @@ PHP_FUNCTION(mcrypt_cbc)
 PHP_FUNCTION(mcrypt_cfb)
 {
 	zval **mode;
 	char *cipher, *key, *data, *iv = NULL;
 	int cipher_len, key_len, data_len, iv_len = 0;
-	
+
 	MCRYPT_GET_CRYPT_ARGS
 
 	convert_to_long_ex(mode);
 
 	php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "cfb", iv, iv_len, ZEND_NUM_ARGS(), Z_LVAL_PP(mode), return_value TSRMLS_CC);
@@ -1372,11 +1380,11 @@ PHP_FUNCTION(mcrypt_cfb)
 PHP_FUNCTION(mcrypt_ofb)
 {
 	zval **mode;
 	char *cipher, *key, *data, *iv = NULL;
 	int cipher_len, key_len, data_len, iv_len = 0;
-	
+
 	MCRYPT_GET_CRYPT_ARGS
 
 	convert_to_long_ex(mode);
 
 	php_mcrypt_do_crypt(cipher, key, key_len, data, data_len, "ofb", iv, iv_len, ZEND_NUM_ARGS(), Z_LVAL_PP(mode), return_value TSRMLS_CC);
@@ -1398,13 +1406,13 @@ PHP_FUNCTION(mcrypt_create_iv)
 
 	if (size <= 0 || size >= INT_MAX) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create an IV with a size of less than 1 or greater than %d", INT_MAX);
 		RETURN_FALSE;
 	}
-	
+
 	iv = ecalloc(size + 1, 1);
-	
+
 	if (source == RANDOM || source == URANDOM) {
 #if PHP_WIN32
 		/* random/urandom equivalent on Windows */
 		BYTE *iv_b = (BYTE *) iv;
 		if (php_win32_get_random_bytes(iv_b, (size_t) size) == FAILURE){

Vulnerable code: typedef struct _php_mcrypt { 
	"mcrypt", 
	ZEND_FETCH_RESOURCE (pm, php_mcrypt *, &mcryptind, -1, "MCrypt", le_mcrypt);				
	if (pm) {	
	
   
	
		mode, 
	
	
	
	
	
	
	
		RETURN_TRUE 
	
	
	
	
	
	
	
	
	int   cipher_len, module_len; 
	
	int   cipher_len, module_len; 
	
	int   cipher_len, module_len; 
	
			if (key_length_sizes[i] >= key_len && 
	
	
	
	
	
	
	
	
	
	
	
Fixed code: typedef struct _php_mcrypt {
	"mcrypt",
	ZEND_FETCH_RESOURCE (pm, php_mcrypt *, &mcryptind, -1, "MCrypt", le_mcrypt);
	if (pm) {



		mode,


		if (data_size <= 0) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Integer overflow in data size");
			RETURN_FALSE;
		}



		if (data_size <= 0) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Integer overflow in data size");
			RETURN_FALSE;
		}


		RETURN_TRUE








	int   cipher_len, module_len;

	int   cipher_len, module_len;

	int   cipher_len, module_len;

			if (key_length_sizes[i] >= key_len &&