andrey                                   Tue, 27 Apr 2010 12:32:34 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=298657

Log:
Fixed very rare memory leak in mysqlnd, when binding thousands of columns

Changed paths:
    U   php/php-src/branches/PHP_5_3/NEWS
    A   
php/php-src/branches/PHP_5_3/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt
    U   php/php-src/branches/PHP_5_3/ext/mysqlnd/mysqlnd_ps_codec.c
    A   
php/php-src/trunk/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt
    U   php/php-src/trunk/ext/mysqlnd/mysqlnd_ps_codec.c

Modified: php/php-src/branches/PHP_5_3/NEWS
===================================================================
--- php/php-src/branches/PHP_5_3/NEWS	2010-04-27 12:27:15 UTC (rev 298656)
+++ php/php-src/branches/PHP_5_3/NEWS	2010-04-27 12:32:34 UTC (rev 298657)
@@ -16,8 +16,10 @@

 - Implemented FR#35638 (Adding udate to imap_fetch_overview results).
   (Charles_Duffy at dell dot com )
-- Fixed possible buffer overflows in mysqlnd_list_fields,  mysqlnd_change_user
+- Fixed possible buffer overflows in mysqlnd_list_fields,  mysqlnd_change_user.
   (Andrey)
+- Fixed very rare memory leak in mysqlnd, when binding thousands of columns.
+  (Andrey)

 - Fixed handling of session variable serialization on certain prefix
   characters. Reported by Stefan Esser (Ilia)

Added: php/php-src/branches/PHP_5_3/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt
===================================================================
--- php/php-src/branches/PHP_5_3/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_3/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt	2010-04-27 12:32:34 UTC (rev 298657)
@@ -0,0 +1,76 @@
+--TEST--
+mysqli_stmt_bind_param() - Binding with very high number of columns
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifemb.inc');
+require_once('skipifconnectfailure.inc');
+?>
+--FILE--
+<?php
+	/*
+	The way we test the INSERT and data types overlaps with
+	the mysqli_stmt_bind_result test in large parts. There is only
+	one difference. This test uses mysqli_query()/mysqli_fetch_assoc() to
+	fetch the inserted values. This way we test
+	mysqli_query()/mysqli_fetch_assoc() for all possible data types
+	in this file and we test mysqli_stmt_bind_result() in the other
+	test -- therefore the "duplicate" makes some sense to me.
+	*/
+	require_once("connect.inc");
+
+	if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
+		printf("Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
+			$host, $user, $db, $port, $socket);
+		exit(1);
+	}
+
+	if (!mysqli_query($link, 'DROP TABLE IF EXISTS ps_test')) {
+		printf("Failed to drop old test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+		exit(1);
+	}
+
+	$cols = 2500;
+	$str = array();
+	for ($i = 1; $i <= $cols; $i++) {
+		$str[] ="a$i INT";
+	}
+	$link->query("CREATE TABLE ps_test(" . implode(" , ", $str) . ")");
+	if (mysqli_errno($link)) {
+		printf("Failed to create the test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+		die("");
+	}
+	$stmt = $link->prepare("INSERT INTO ps_test VALUES(".str_repeat("?, ", $cols-1) . "?)");
+	var_dump($stmt->id);
+	$eval_str="\$stmt->bind_param(\"".str_repeat("i",$cols)."\", ";
+	for ($i = 1; $i < $cols; $i++) {
+		$eval_str.="\$i,";
+	}
+	$eval_str.="\$i";
+	$eval_str.=");";
+	eval($eval_str);
+	var_dump($stmt->execute());
+
+	mysqli_stmt_close($stmt);
+
+
+	mysqli_close($link);
+
+	print "done!";
+?>
+--CLEAN--
+<?php
+	if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
+		printf("Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
+			$host, $user, $db, $port, $socket);
+		exit(1);
+	}
+	if (!mysqli_query($link, 'DROP TABLE IF EXISTS ps_test')) {
+		printf("Failed to drop the test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+		exit(1);
+	}
+?>
+--EXPECTF--
+int(1)
+bool(true)
+done!

Modified: php/php-src/branches/PHP_5_3/ext/mysqlnd/mysqlnd_ps_codec.c
===================================================================
--- php/php-src/branches/PHP_5_3/ext/mysqlnd/mysqlnd_ps_codec.c	2010-04-27 12:27:15 UTC (rev 298656)
+++ php/php-src/branches/PHP_5_3/ext/mysqlnd/mysqlnd_ps_codec.c	2010-04-27 12:32:34 UTC (rev 298657)
@@ -599,6 +599,7 @@
 {
 	MYSQLND_STMT_DATA * stmt = s->data;
 	unsigned int i = 0;
+	zend_uchar * provided_buffer = *buf;
 	size_t left = (*buf_len - (*p - *buf));
 	size_t data_size = 0;
 	zval **copies = NULL;/* if there are different types */
@@ -714,9 +715,17 @@
 		*buf_len = offset + data_size + 10; /* Allocate + 10 for safety */
 		tmp_buf = mnd_emalloc(*buf_len);
 		memcpy(tmp_buf, *buf, offset);
+		/*
+		  When too many columns the buffer provided to the function might not be sufficient.
+		  In this case new buffer has been allocated above. When we allocate a buffer and then
+		  allocate a bigger one here, we should free the first one.
+		*/
+		if (*buf != provided_buffer) {
+			mnd_efree(*buf);
+		}
 		*buf = tmp_buf;
 		/* Update our pos pointer */
-		*p = *buf + offset;
+		*p = *buf + offset;
 	}

 	/* 2.3 Store the actual data */

Added: php/php-src/trunk/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt
===================================================================
--- php/php-src/trunk/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/mysqli/tests/mysqli_stmt_bind_param_many_columns.phpt	2010-04-27 12:32:34 UTC (rev 298657)
@@ -0,0 +1,76 @@
+--TEST--
+mysqli_stmt_bind_param() - Binding with very high number of columns
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifemb.inc');
+require_once('skipifconnectfailure.inc');
+?>
+--FILE--
+<?php
+	/*
+	The way we test the INSERT and data types overlaps with
+	the mysqli_stmt_bind_result test in large parts. There is only
+	one difference. This test uses mysqli_query()/mysqli_fetch_assoc() to
+	fetch the inserted values. This way we test
+	mysqli_query()/mysqli_fetch_assoc() for all possible data types
+	in this file and we test mysqli_stmt_bind_result() in the other
+	test -- therefore the "duplicate" makes some sense to me.
+	*/
+	require_once("connect.inc");
+
+	if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
+		printf("Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
+			$host, $user, $db, $port, $socket);
+		exit(1);
+	}
+
+	if (!mysqli_query($link, 'DROP TABLE IF EXISTS ps_test')) {
+		printf("Failed to drop old test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+		exit(1);
+	}
+
+	$cols = 2500;
+	$str = array();
+	for ($i = 1; $i <= $cols; $i++) {
+		$str[] ="a$i INT";
+	}
+	$link->query("CREATE TABLE ps_test(" . implode(" , ", $str) . ")");
+	if (mysqli_errno($link)) {
+		printf("Failed to create the test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+		die("");
+	}
+	$stmt = $link->prepare("INSERT INTO ps_test VALUES(".str_repeat("?, ", $cols-1) . "?)");
+	var_dump($stmt->id);
+	$eval_str="\$stmt->bind_param(\"".str_repeat("i",$cols)."\", ";
+	for ($i = 1; $i < $cols; $i++) {
+		$eval_str.="\$i,";
+	}
+	$eval_str.="\$i";
+	$eval_str.=");";
+	eval($eval_str);
+	var_dump($stmt->execute());
+
+	mysqli_stmt_close($stmt);
+
+
+	mysqli_close($link);
+
+	print "done!";
+?>
+--CLEAN--
+<?php
+	if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
+		printf("Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
+			$host, $user, $db, $port, $socket);
+		exit(1);
+	}
+	if (!mysqli_query($link, 'DROP TABLE IF EXISTS ps_test')) {
+		printf("Failed to drop the test table: [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+		exit(1);
+	}
+?>
+--EXPECTF--
+int(1)
+bool(true)
+done!

Modified: php/php-src/trunk/ext/mysqlnd/mysqlnd_ps_codec.c
===================================================================
--- php/php-src/trunk/ext/mysqlnd/mysqlnd_ps_codec.c	2010-04-27 12:27:15 UTC (rev 298656)
+++ php/php-src/trunk/ext/mysqlnd/mysqlnd_ps_codec.c	2010-04-27 12:32:34 UTC (rev 298657)
@@ -599,6 +599,7 @@
 {
 	MYSQLND_STMT_DATA * stmt = s->data;
 	unsigned int i = 0;
+	zend_uchar * provided_buffer = *buf;
 	size_t left = (*buf_len - (*p - *buf));
 	size_t data_size = 0;
 	zval **copies = NULL;/* if there are different types */
@@ -714,9 +715,17 @@
 		*buf_len = offset + data_size + 10; /* Allocate + 10 for safety */
 		tmp_buf = mnd_emalloc(*buf_len);
 		memcpy(tmp_buf, *buf, offset);
+		/*
+		  When too many columns the buffer provided to the function might not be sufficient.
+		  In this case new buffer has been allocated above. When we allocate a buffer and then
+		  allocate a bigger one here, we should free the first one.
+		*/
+		if (*buf != provided_buffer) {
+			mnd_efree(*buf);
+		}
 		*buf = tmp_buf;
 		/* Update our pos pointer */
-		*p = *buf + offset;
+		*p = *buf + offset;
 	}

 	/* 2.3 Store the actual data */
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to