Skip to content

Commit

Permalink
Fix for bug #71863 Segfault when EXPLAIN with "Unknown column" error
Browse files Browse the repository at this point in the history
The reason was that after the big refactoring of mysqlnd at the end of
last year code that is initializing the error_info structure in the
result set was not added. It existed already for connections and PS.
The code that segfaults is hit only with MariaDB because MariaDB sends
full metadata about the EXPLAIN query + EOF packet and only then it sends
an error packet. MySQL doesn't do that but sends directly an error which
is caught (by different code path). As errors during execution (which means
after sending meta) are pretty rare there was no test case of MySQL to
catch it.
  • Loading branch information
faizshukri authored and nikic committed Jul 25, 2016
1 parent ac0bbea commit b27ff62
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 3 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ PHP NEWS
. Fixed bug #72658 (Locale::lookup() / locale_lookup() hangs if no match
found). (Anatol)

- Mysqlnd:
. Fixed bug #71863 (Segfault when EXPLAIN with "Unknown column" error when
using MariaDB). (Andrey)

- Reflection:
. Fixed bug #72661 (ReflectionType::__toString crashes with iterable).
(Laruence)
Expand Down
37 changes: 37 additions & 0 deletions ext/mysqli/tests/bug71863.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Bug #71863 Segfault when EXPLAIN with "Unknown Column" Error
--SKIPIF--
<?php
require_once('skipif.inc');
require_once('skipifconnectfailure.inc');
require_once("connect.inc");
if (!$IS_MYSQLND) {
die("skip mysqlnd only test");
}
?>
--FILE--
<?php
require_once("connect.inc");

$req = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket);

// create db and table for test
mysqli_query($req, "DROP TABLE IF EXISTS test_bug_71863") or die(mysqli_error($req));
mysqli_query($req, "CREATE TABLE test_bug_71863 (id INT UNSIGNED NOT NULL DEFAULT 0)") or die(mysqli_error($req));

// segfault if EXPLAIN + "Unknown column" error
mysqli_query($req, "EXPLAIN SELECT `id` FROM `test_bug_71863` WHERE `owner_id` = '2' AND `object_id` = '1' AND type = '0'") or die(mysqli_error($req)."\n");

?>
--CLEAN--
<?php
require_once("connect.inc");
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
printf("[c001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
if (!mysqli_query($link, "DROP TABLE IF EXISTS test_bug_71863"))
printf("[c002] Cannot drop table, [%d] %s\n", mysqli_errno($link), mysqli_error($link));
mysqli_close($link);
?>
--EXPECTF--
Warning: mysqli_query(): (42S22/1054): Unknown column 'owner_id' in 'where clause' in %sbug71863.php on line %d
Unknown column 'owner_id' in 'where clause'
4 changes: 2 additions & 2 deletions ext/mysqlnd/mysqlnd_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,11 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_connection_state)
MYSQLND_CLASS_METHODS_END;


/* {{{ mysqlnd_upsert_status_init */
/* {{{ mysqlnd_connection_state_init */
PHPAPI void
mysqlnd_connection_state_init(struct st_mysqlnd_connection_state * const state)
{
DBG_ENTER("mysqlnd_error_info_init");
DBG_ENTER("mysqlnd_connection_state_init");
state->m = &MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_connection_state);
state->state = CONN_ALLOCED;
DBG_VOID_RETURN;
Expand Down
11 changes: 10 additions & 1 deletion ext/mysqlnd/mysqlnd_result.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons
DBG_ENTER("mysqlnd_result_buffered::free_result");
DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count);

mysqlnd_error_info_free_contents(&set->error_info);

if (set->type == MYSQLND_BUFFERED_TYPE_ZVAL) {
MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)((MYSQLND_RES_BUFFERED_ZVAL *) set);
} if (set->type == MYSQLND_BUFFERED_TYPE_C) {
Expand Down Expand Up @@ -1954,7 +1956,6 @@ mysqlnd_result_unbuffered_init(const unsigned int field_count, const zend_bool p
if (!ret) {
DBG_RETURN(NULL);
}

if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
Expand Down Expand Up @@ -1995,6 +1996,10 @@ mysqlnd_result_buffered_zval_init(const unsigned int field_count, const zend_boo
if (!ret) {
DBG_RETURN(NULL);
}
if (FAIL == mysqlnd_error_info_init(&ret->error_info, persistent)) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
Expand Down Expand Up @@ -2038,6 +2043,10 @@ mysqlnd_result_buffered_c_init(const unsigned int field_count, const zend_bool p
if (!ret) {
DBG_RETURN(NULL);
}
if (FAIL == mysqlnd_error_info_init(&ret->error_info, persistent)) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
}
if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(size_t), persistent))) {
mnd_pefree(ret, persistent);
DBG_RETURN(NULL);
Expand Down

0 comments on commit b27ff62

Please sign in to comment.