From c38ede752305d151ca1a30121099324dcf7fb91a Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 12 Jan 2020 17:39:19 +0100 Subject: [PATCH 1/2] Fix #79084: mysqlnd may fetch wrong column indexes with MYSQLI_BOTH Column names can be numeric strings, so we have to make sure to insert the column values with the appropriate numeric keys, instead of adding them. --- ext/mysqli/tests/bug79084.phpt | 79 ++++++++++++++++++++++++++++++++++ ext/mysqlnd/mysqlnd_result.c | 6 +-- 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 ext/mysqli/tests/bug79084.phpt diff --git a/ext/mysqli/tests/bug79084.phpt b/ext/mysqli/tests/bug79084.phpt new file mode 100644 index 0000000000000..b760c4abb7659 --- /dev/null +++ b/ext/mysqli/tests/bug79084.phpt @@ -0,0 +1,79 @@ +--TEST-- +Bug #79084 (mysqlnd may fetch wrong column indexes with MYSQLI_BOTH) +--SKIPIF-- + +--FILE-- +real_query($sql); +$res = $link->use_result(); +$row = $res->fetch_array(); +var_dump($row); +$link->close(); + +// buffered +ini_set('mysqlnd.fetch_data_copy', false); +$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); +$res = $link->query($sql); +$row = $res->fetch_array(); +var_dump($row); +$link->close(); + +// buffered copies +ini_set('mysqlnd.fetch_data_copy', true); +$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); +$res = $link->query($sql); +$row = $res->fetch_array(); +var_dump($row); +$link->close(); +?> +--EXPECT-- +array(6) { + [0]=> + string(1) "0" + [2007]=> + string(1) "0" + [1]=> + string(1) "0" + [2008]=> + string(1) "0" + [2]=> + string(1) "0" + [2020]=> + string(1) "0" +} +array(6) { + [0]=> + string(1) "0" + [2007]=> + string(1) "0" + [1]=> + string(1) "0" + [2008]=> + string(1) "0" + [2]=> + string(1) "0" + [2020]=> + string(1) "0" +} +array(6) { + [0]=> + string(1) "0" + [2007]=> + string(1) "0" + [1]=> + string(1) "0" + [2008]=> + string(1) "0" + [2]=> + string(1) "0" + [2020]=> + string(1) "0" +} diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 783fd2cec10d1..6b459304fd097 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -843,7 +843,7 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void if (flags & MYSQLND_FETCH_NUM) { Z_TRY_ADDREF_P(data); - zend_hash_next_index_insert(row_ht, data); + zend_hash_index_add(row_ht, i, data); } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ @@ -1100,7 +1100,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, vo if (flags & MYSQLND_FETCH_NUM) { Z_TRY_ADDREF_P(data); - zend_hash_next_index_insert(Z_ARRVAL_P(row), data); + zend_hash_index_add(Z_ARRVAL_P(row), i, data); } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ @@ -1196,7 +1196,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void if (flags & MYSQLND_FETCH_NUM) { Z_TRY_ADDREF_P(data); - zend_hash_next_index_insert(Z_ARRVAL_P(row), data); + zend_hash_index_add(Z_ARRVAL_P(row), i, data); } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ From 1f9b97fa926809e576770a1ed44f26d30ea907a1 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Mon, 13 Jan 2020 11:47:40 +0100 Subject: [PATCH 2/2] Only try to increase refcount if value is actually added --- ext/mysqli/tests/bug79084_collision.phpt | 61 ++++++++++++++++++++++++ ext/mysqlnd/mysqlnd_result.c | 15 +++--- 2 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 ext/mysqli/tests/bug79084_collision.phpt diff --git a/ext/mysqli/tests/bug79084_collision.phpt b/ext/mysqli/tests/bug79084_collision.phpt new file mode 100644 index 0000000000000..9f0c72962d55d --- /dev/null +++ b/ext/mysqli/tests/bug79084_collision.phpt @@ -0,0 +1,61 @@ +--TEST-- +Bug #79084 (mysqlnd may fetch wrong column indexes with MYSQLI_BOTH) - collision +--SKIPIF-- + +--FILE-- +real_query($sql); +$res = $link->use_result(); +$row = $res->fetch_array(); +var_dump($row); +$link->close(); + +// buffered +ini_set('mysqlnd.fetch_data_copy', false); +$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); +$res = $link->query($sql); +$row = $res->fetch_array(); +var_dump($row); +$link->close(); + +// buffered copies +ini_set('mysqlnd.fetch_data_copy', true); +$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); +$res = $link->query($sql); +$row = $res->fetch_array(); +var_dump($row); +$link->close(); +?> +--EXPECT-- +array(3) { + [0]=> + string(5) "11111" + [1]=> + string(5) "11111" + [2]=> + string(5) "22222" +} +array(3) { + [0]=> + string(5) "11111" + [1]=> + string(5) "11111" + [2]=> + string(5) "22222" +} +array(3) { + [0]=> + string(5) "11111" + [1]=> + string(5) "11111" + [2]=> + string(5) "22222" +} diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 6b459304fd097..10b4d099054f0 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -842,8 +842,9 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void const size_t len = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0; if (flags & MYSQLND_FETCH_NUM) { - Z_TRY_ADDREF_P(data); - zend_hash_index_add(row_ht, i, data); + if (zend_hash_index_add(row_ht, i, data) != NULL) { + Z_TRY_ADDREF_P(data); + } } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ @@ -1099,8 +1100,9 @@ MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, vo set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0; if (flags & MYSQLND_FETCH_NUM) { - Z_TRY_ADDREF_P(data); - zend_hash_index_add(Z_ARRVAL_P(row), i, data); + if (zend_hash_index_add(Z_ARRVAL_P(row), i, data) != NULL) { + Z_TRY_ADDREF_P(data); + } } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */ @@ -1195,8 +1197,9 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void set->lengths[i] = (Z_TYPE_P(data) == IS_STRING)? Z_STRLEN_P(data) : 0; if (flags & MYSQLND_FETCH_NUM) { - Z_TRY_ADDREF_P(data); - zend_hash_index_add(Z_ARRVAL_P(row), i, data); + if (zend_hash_index_add(Z_ARRVAL_P(row), i, data)) { + Z_TRY_ADDREF_P(data); + } } if (flags & MYSQLND_FETCH_ASSOC) { /* zend_hash_quick_update needs length + trailing zero */