From 74ba88e186a0f70caa6e8e5a440a3a204afabf50 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Tue, 26 Nov 2013 19:01:49 +0200 Subject: [PATCH 1/3] Fix for Bug #66141 (mysqlnd quote function is wrong with NO_BACKSLASH_ESCAPES after failed query) --- NEWS | 2 ++ ext/mysqlnd/mysqlnd.c | 12 ++++++++---- ext/mysqlnd/mysqlnd_ps.c | 3 +++ ext/mysqlnd/mysqlnd_result.c | 4 ++++ ext/mysqlnd/mysqlnd_wireprotocol.c | 2 ++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index c5b59963281..5c58f91e160 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,8 @@ PHP NEWS - mysqlnd: . Fixed bug #66124 (mysqli under mysqlnd loses precision when bind_param with 'i'). (Andrey) + . Fixed bug #66141 (mysqlnd quote function is wrong with NO_BACKSLASH_ESCAPES + after failed query). (Andrey) - PDO . Fixed bug 65946 (sql_parser permanently converts values bound to strings) diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 6eb34e40922..20e63e1405c 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -251,6 +251,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, simple_command_handle_response)(MYSQLND_CONN_D conn->persistent); if (!ignore_upsert_status) { + memset(conn->upsert_status, 0, sizeof(*conn->upsert_status)); conn->upsert_status->warning_count = ok_response->warning_count; conn->upsert_status->server_status = ok_response->server_status; conn->upsert_status->affected_rows = ok_response->affected_rows; @@ -314,6 +315,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum DBG_ENTER("mysqlnd_conn_data::simple_command"); DBG_INF_FMT("command=%s ok_packet=%u silent=%u", mysqlnd_command_to_text[command], ok_packet, silent); + DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); switch (CONN_GET_STATE(conn)) { case CONN_READY: @@ -328,10 +330,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, simple_command)(MYSQLND_CONN_DATA * conn, enum DBG_RETURN(FAIL); } - /* clean UPSERT info */ - if (!ignore_upsert_status) { - memset(conn->upsert_status, 0, sizeof(*conn->upsert_status)); - } SET_ERROR_AFF_ROWS(conn); SET_EMPTY_ERROR(*conn->error_info); @@ -888,6 +886,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn, conn->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE; /* todo: check if charset is available */ conn->server_capabilities = greet_packet->server_capabilities; + memset(conn->upsert_status, 0, sizeof(*conn->upsert_status)); conn->upsert_status->warning_count = 0; conn->upsert_status->server_status = greet_packet->server_status; conn->upsert_status->affected_rows = 0; @@ -1064,6 +1063,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const ch enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_conn_data::send_query"); DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query); + DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { ret = conn->m->simple_command(conn, COM_QUERY, (zend_uchar *) query, query_len, @@ -1074,6 +1074,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, send_query)(MYSQLND_CONN_DATA * conn, const ch } conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC); } + DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); DBG_RETURN(ret); } /* }}} */ @@ -1089,6 +1090,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC DBG_ENTER("mysqlnd_conn_data::reap_query"); DBG_INF_FMT("conn=%llu", conn->thread_id); + DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { if (state <= CONN_READY || state == CONN_QUIT_SENT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed"); @@ -1099,6 +1101,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, reap_query)(MYSQLND_CONN_DATA * conn TSRMLS_DC conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC); } + DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); DBG_RETURN(ret); } /* }}} */ @@ -1477,6 +1480,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, escape_string)(MYSQLND_CONN_DATA * const conn, DBG_INF_FMT("conn=%llu", conn->thread_id); if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { + DBG_INF_FMT("server_status=%u", conn->upsert_status->server_status); if (conn->upsert_status->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) { ret = mysqlnd_cset_escape_quotes(conn->charset, newstr, escapestr, escapestr_len TSRMLS_CC); } else { diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index cd5b3022758..3a1f6a05217 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -485,6 +485,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s TSRMLS_DC) ret = mysqlnd_query_read_result_set_header(stmt->conn, s TSRMLS_CC); if (ret == FAIL) { COPY_CLIENT_ERROR(*stmt->error_info, *conn->error_info); + memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status)); stmt->upsert_status->affected_rows = conn->upsert_status->affected_rows; if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) { /* close the statement here, the connection has been closed */ @@ -913,6 +914,7 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES *result, void *param, unsigned int DBG_INF("EOF"); /* Mark the connection as usable again */ result->unbuf->eof_reached = TRUE; + memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status)); result->conn->upsert_status->warning_count = row_packet->warning_count; result->conn->upsert_status->server_status = row_packet->server_status; /* @@ -1022,6 +1024,7 @@ mysqlnd_fetch_stmt_row_cursor(MYSQLND_RES *result, void *param, unsigned int fla row_packet->skip_extraction = stmt->result_bind? FALSE:TRUE; + memset(stmt->upsert_status, 0, sizeof(*stmt->upsert_status)); if (PASS == (ret = PACKET_READ(row_packet, result->conn)) && !row_packet->eof) { unsigned int i, field_count = result->field_count; diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index fd2023cd10c..56790b0bc16 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -426,6 +426,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s DBG_INF("UPSERT"); conn->last_query_type = QUERY_UPSERT; conn->field_count = rset_header->field_count; + memset(conn->upsert_status, 0, sizeof(*conn->upsert_status)); conn->upsert_status->warning_count = rset_header->warning_count; conn->upsert_status->server_status = rset_header->server_status; conn->upsert_status->affected_rows = rset_header->affected_rows; @@ -714,6 +715,7 @@ mysqlnd_fetch_row_unbuffered_c(MYSQLND_RES * result TSRMLS_DC) /* Mark the connection as usable again */ DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status); result->unbuf->eof_reached = TRUE; + memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status)); result->conn->upsert_status->warning_count = row_packet->warning_count; result->conn->upsert_status->server_status = row_packet->server_status; /* @@ -849,6 +851,7 @@ mysqlnd_fetch_row_unbuffered(MYSQLND_RES * result, void *param, unsigned int fla /* Mark the connection as usable again */ DBG_INF_FMT("warnings=%u server_status=%u", row_packet->warning_count, row_packet->server_status); result->unbuf->eof_reached = TRUE; + memset(result->conn->upsert_status, 0, sizeof(*result->conn->upsert_status)); result->conn->upsert_status->warning_count = row_packet->warning_count; result->conn->upsert_status->server_status = row_packet->server_status; /* @@ -1206,6 +1209,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c /* Finally clean */ if (row_packet->eof) { + memset(conn->upsert_status, 0, sizeof(*conn->upsert_status)); conn->upsert_status->warning_count = row_packet->warning_count; conn->upsert_status->server_status = row_packet->server_status; } diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index d0ab9fe7312..4f456a3ce7d 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -763,6 +763,7 @@ php_mysqlnd_ok_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC) packet->error, sizeof(packet->error), &packet->error_no, packet->sqlstate TSRMLS_CC); + DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); DBG_RETURN(PASS); } /* Everything was fine! */ @@ -1005,6 +1006,7 @@ php_mysqlnd_rset_header_read(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC) packet->error_info.error, sizeof(packet->error_info.error), &packet->error_info.error_no, packet->error_info.sqlstate TSRMLS_CC); + DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); DBG_RETURN(PASS); } From 0aadab0e2eb101953ceb60b63f6ac157f7e03b03 Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Tue, 26 Nov 2013 19:15:12 +0200 Subject: [PATCH 2/3] add test --- ext/pdo_mysql/tests/bug66141.phpt | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 ext/pdo_mysql/tests/bug66141.phpt diff --git a/ext/pdo_mysql/tests/bug66141.phpt b/ext/pdo_mysql/tests/bug66141.phpt new file mode 100644 index 00000000000..3a28509314e --- /dev/null +++ b/ext/pdo_mysql/tests/bug66141.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #66141 (mysqlnd quote function is wrong with NO_BACKSLASH_ESCAPES after failed query) +--SKIPIF-- + +--FILE-- +quote($input); + +$db->query('set session sql_mode="NO_BACKSLASH_ESCAPES"'); + +// injection text from some user input + +$quotedInput1 = $db->quote($input); + +$db->query('something that throws an exception'); + +$quotedInput2 = $db->quote($input); + +var_dump($quotedInput0); +var_dump($quotedInput1); +var_dump($quotedInput2); +?> +done +--EXPECTF-- +Warning: PDO::query(): SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'something that throws an exception' at line %d in %s on line %d +string(50) "'Something\', 1 as one, 2 as two FROM dual; -- f'" +string(50) "'Something'', 1 as one, 2 as two FROM dual; -- f'" +string(50) "'Something'', 1 as one, 2 as two FROM dual; -- f'" +done \ No newline at end of file From 5a3886f15c010584eecd0ff21eeca72359539fea Mon Sep 17 00:00:00 2001 From: Andrey Hristov Date: Tue, 26 Nov 2013 19:15:30 +0200 Subject: [PATCH 3/3] merge fix --- ext/mysqlnd/mysqlnd.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index ead73065fcf..6e17412edb5 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -318,14 +318,9 @@ MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request)(MYSQLND_CONN_DATA enum_func_status ret = PASS; MYSQLND_PACKET_COMMAND * cmd_packet; -<<<<<<< HEAD DBG_ENTER("mysqlnd_conn_data::simple_command_send_request"); DBG_INF_FMT("command=%s silent=%u", mysqlnd_command_to_text[command], silent); -======= - DBG_ENTER("mysqlnd_conn_data::simple_command"); - DBG_INF_FMT("command=%s ok_packet=%u silent=%u", mysqlnd_command_to_text[command], ok_packet, silent); DBG_INF_FMT("conn->server_status=%u", conn->upsert_status->server_status); ->>>>>>> PHP-5.4 switch (CONN_GET_STATE(conn)) { case CONN_READY: @@ -814,6 +809,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn, { goto err; } + memset(conn->upsert_status, 0, sizeof(*conn->upsert_status)); conn->upsert_status->warning_count = 0; conn->upsert_status->server_status = greet_packet->server_status; conn->upsert_status->affected_rows = 0; @@ -1038,14 +1034,6 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect)(MYSQLND_CONN_DATA * conn, } conn->max_packet_size = MYSQLND_ASSEMBLED_PACKET_MAX_SIZE; /* todo: check if charset is available */ -<<<<<<< HEAD -======= - conn->server_capabilities = greet_packet->server_capabilities; - memset(conn->upsert_status, 0, sizeof(*conn->upsert_status)); - conn->upsert_status->warning_count = 0; - conn->upsert_status->server_status = greet_packet->server_status; - conn->upsert_status->affected_rows = 0; ->>>>>>> PHP-5.4 SET_EMPTY_ERROR(*conn->error_info);