Skip to content

Commit 934691f

Browse files
committed
Fixed bug #77597
The same variable was reused in two nested loops... The test doesn't fail on 7.2, but I'm fixing this here anyway as the code is clearly wrong, and probably erroneous in other situations.
1 parent 74888be commit 934691f

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? 2019, PHP 7.2.17
44

5+
- MySQLi:
6+
. Fixed bug #77597 (mysqli_fetch_field hangs scripts). (Nikita)
7+
58
07 Mar 2019, PHP 7.2.16
69

710
- Core:

ext/mysqli/tests/bug77597.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Bug #77597: mysqli_fetch_field hangs scripts
3+
--SKIPIF--
4+
<?php
5+
require_once('skipif.inc');
6+
require_once('skipifconnectfailure.inc');
7+
?>
8+
--FILE--
9+
<?php
10+
11+
require_once("connect.inc");
12+
$mysqli = new my_mysqli($host, $user, $passwd, $db, $port, $socket);
13+
14+
$mysqli->query('DROP TABLE IF EXISTS a');
15+
$mysqli->query('CREATE TABLE a (b int)');
16+
$mysqli->query('INSERT INTO a VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9)');
17+
18+
$mysqli->real_query("SELECT * FROM a");
19+
20+
$result = $mysqli->store_result(MYSQLI_STORE_RESULT_COPY_DATA);
21+
22+
$field = $result->fetch_field();
23+
var_dump($field->name);
24+
25+
?>
26+
--EXPECT--
27+
string(1) "b"

ext/mysqlnd/mysqlnd_result.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RE
9191
MYSQLND_STATS * stats,
9292
zend_bool int_and_float_native)
9393
{
94-
unsigned int i;
94+
unsigned int row, field;
9595
enum_func_status ret = PASS;
9696
const unsigned int field_count = meta->field_count;
9797
const uint64_t row_count = result->row_count;
@@ -106,33 +106,33 @@ MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RE
106106
DBG_RETURN(FAIL);
107107
}
108108

109-
for (i = 0; i < result->row_count; i++) {
109+
for (row = 0; row < result->row_count; row++) {
110110
/* (i / 8) & the_bit_for_i*/
111-
if (initialized[i >> 3] & (1 << (i & 7))) {
111+
if (initialized[row >> 3] & (1 << (row & 7))) {
112112
continue;
113113
}
114114

115-
rc = result->m.row_decoder(result->row_buffers[i], current_row, field_count, meta->fields, int_and_float_native, stats);
115+
rc = result->m.row_decoder(result->row_buffers[row], current_row, field_count, meta->fields, int_and_float_native, stats);
116116

117117
if (rc != PASS) {
118118
ret = FAIL;
119119
break;
120120
}
121121
result->initialized_rows++;
122-
initialized[i >> 3] |= (1 << (i & 7));
123-
for (i = 0; i < field_count; i++) {
122+
initialized[row >> 3] |= (1 << (row & 7));
123+
for (field = 0; field < field_count; field++) {
124124
/*
125125
NULL fields are 0 length, 0 is not more than 0
126126
String of zero size, definitely can't be the next max_length.
127127
Thus for NULL and zero-length we are quite efficient.
128128
*/
129-
if (Z_TYPE(current_row[i]) == IS_STRING) {
130-
const size_t len = Z_STRLEN(current_row[i]);
131-
if (meta->fields[i].max_length < len) {
132-
meta->fields[i].max_length = len;
129+
if (Z_TYPE(current_row[field]) == IS_STRING) {
130+
const size_t len = Z_STRLEN(current_row[field]);
131+
if (meta->fields[field].max_length < len) {
132+
meta->fields[field].max_length = len;
133133
}
134134
}
135-
zval_ptr_dtor_nogc(&current_row[i]);
135+
zval_ptr_dtor_nogc(&current_row[field]);
136136
}
137137
}
138138
mnd_efree(current_row);

0 commit comments

Comments
 (0)