]> granicus.if.org Git - php/commitdiff
Fix for bug #71863 Segfault when EXPLAIN with "Unknown column" error
authorAndrey Hristov <andrey@php.net>
Fri, 22 Jul 2016 21:26:26 +0000 (00:26 +0300)
committerNikita Popov <nikic@php.net>
Mon, 25 Jul 2016 19:59:27 +0000 (21:59 +0200)
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.

NEWS
ext/mysqli/tests/bug71863.phpt [new file with mode: 0644]
ext/mysqlnd/mysqlnd_connection.c
ext/mysqlnd/mysqlnd_result.c

diff --git a/NEWS b/NEWS
index 69728b4aec6d5755b0d09920c1d18f671bc3078f..70d667af03456cc430de96f90416bcfabf40ba33 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -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)
diff --git a/ext/mysqli/tests/bug71863.phpt b/ext/mysqli/tests/bug71863.phpt
new file mode 100644 (file)
index 0000000..8897928
--- /dev/null
@@ -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'
\ No newline at end of file
index 701bc282e11bedc5f557e1e7e2e5336fb9cfcd92..a3300dc6d90bd6d736cdfbbfa691f75bd816fece 100644 (file)
@@ -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;
index c4aa537eb07b7acda82edbd2434ec500e94bc323..513214d3fa4035b1dd1c624c40cce5aaeb514882 100644 (file)
@@ -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) {
@@ -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);
@@ -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);
@@ -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);