summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Hristov <andrey@php.net>2009-12-17 12:30:58 +0000
committerAndrey Hristov <andrey@php.net>2009-12-17 12:30:58 +0000
commit22528b14a4c0055589ecc012bf6a6bd4e7bc1f57 (patch)
treedd707999a77395f1362e16e509fa67e137c03ea3
parent4e06ca007f951d3d8e84bab5f40c75d2db4ab4fb (diff)
downloadphp-git-22528b14a4c0055589ecc012bf6a6bd4e7bc1f57.tar.gz
During refactoring of the function mysqlnd_stream_write_w_header() it was
found that there is a bug in the way the data is sent, although a very rare one which will only affect very large queries which have length 16777214. The communication will hang. A way to test it is to execute the following: ./php -r '$c=mysqli_connect("127.0.0.1","root","root","test"); $q="insert into test.tblob values(\"".str_repeat("a",256*256*256-1-34)."\")"; $c->query($q);'
-rw-r--r--NEWS2
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c138
2 files changed, 48 insertions, 92 deletions
diff --git a/NEWS b/NEWS
index ce2ebcad55..cc34dc7993 100644
--- a/NEWS
+++ b/NEWS
@@ -26,13 +26,13 @@ PHP NEWS
- Improved fix for bug #50006 (Segfault caused by uksort()). (Stas)
+- Fixed mysqlnd hang when queries exactly 16777214 bytes long are sent. (Andrey)
- Fixed incorrect decoding of 5-byte BIT sequences in mysqlnd. (Andrey)
- Fixed error_log() to be binary safe when using message_type 3. (Jani)
- Fixed unnecessary invocation of setitimer when timeouts have been disabled.
(Arvind Srinivasan)
- Fixed memory leak in extension loading when an error occurs on Windows.
(Pierre)
-
- Fixed bug #50496 (Use of <stdbool.h> is valid only in a c99 compilation
environment. (Sriram)
- Fixed bug #50464 (declare encoding doesn't work within an included file).
diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c
index 5834eae6e6..cb870be025 100644
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -374,10 +374,11 @@ size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, siz
zend_uchar *safe_storage = safe_buf;
MYSQLND_NET *net = conn->net;
size_t old_chunk_size = net->stream->chunk_size;
- size_t ret, left = count, packets_sent = 1;
+ size_t ret, packets_sent = 1;
+ size_t left = count;
zend_uchar *p = (zend_uchar *) buf;
zend_uchar * compress_buf = NULL;
- size_t comp_buf_size = 0;
+ size_t to_be_sent;
DBG_ENTER("mysqlnd_stream_write_w_header");
DBG_INF_FMT("conn=%llu count=%lu compression=%d", conn->thread_id, count, net->compressed);
@@ -385,37 +386,36 @@ size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, siz
net->stream->chunk_size = MYSQLND_MAX_PACKET_SIZE;
if (net->compressed == TRUE) {
- comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
+ size_t comp_buf_size = MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE + MIN(left, MYSQLND_MAX_PACKET_SIZE);
DBG_INF_FMT("compress_buf_size=%d", comp_buf_size);
compress_buf = emalloc(comp_buf_size);
}
- while (left > MYSQLND_MAX_PACKET_SIZE) {
+ do {
+ to_be_sent = MIN(left, MYSQLND_MAX_PACKET_SIZE);
#ifdef MYSQLND_COMPRESSION_ENABLED
-
if (net->compressed == TRUE) {
-
/* here we need to compress the data and then write it, first comes the compressed header */
- uLong tmp_complen = MYSQLND_MAX_PACKET_SIZE;
+ uLong tmp_complen = to_be_sent;
size_t payload_size;
zend_uchar * uncompressed_payload = p; /* should include the header */
int res;
STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
- int3store(uncompressed_payload, MYSQLND_MAX_PACKET_SIZE);
+ int3store(uncompressed_payload, to_be_sent);
int1store(uncompressed_payload + 3, net->packet_no);
- DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
- res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
+ DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, to_be_sent + MYSQLND_HEADER_SIZE);
+ res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
if (res == Z_OK) {
DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen);
- int3store(compress_buf + MYSQLND_HEADER_SIZE, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
+ int3store(compress_buf + MYSQLND_HEADER_SIZE, to_be_sent + MYSQLND_HEADER_SIZE);
payload_size = tmp_complen;
} else {
DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", res, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
- memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE);
- payload_size = MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE;
+ memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, to_be_sent + MYSQLND_HEADER_SIZE);
+ payload_size = to_be_sent + MYSQLND_HEADER_SIZE;
}
RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
@@ -424,99 +424,55 @@ size_t mysqlnd_stream_write_w_header(MYSQLND * const conn, char * const buf, siz
DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
ret = conn->net->m.stream_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
net->compressed_envelope_packet_no++;
+ #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
+ if (res == Z_OK) {
+ size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
+ uLongf tmp_complen2 = decompressed_size;
+ zend_uchar * decompressed_data = malloc(decompressed_size);
+ int error = uncompress(decompressed_data, &tmp_complen2, compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
+ if (error == Z_OK) {
+ int i;
+ DBG_INF("success decompressing");
+ for (i = 0 ; i < decompressed_size; i++) {
+ if (i && (i % 30 == 0)) {
+ printf("\n\t\t");
+ }
+ printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
+ }
+ } else {
+ DBG_INF("error decompressing");
+ }
+ free(decompressed_data);
+ }
+ #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
} else
#endif /* MYSQLND_COMPRESSION_ENABLED */
{
DBG_INF("no compression");
STORE_HEADER_SIZE(safe_storage, p);
- int3store(p, MYSQLND_MAX_PACKET_SIZE);
+ int3store(p, to_be_sent);
int1store(p + 3, net->packet_no);
- ret = conn->net->m.stream_write(conn, p, MYSQLND_MAX_PACKET_SIZE + MYSQLND_HEADER_SIZE TSRMLS_CC);
+ ret = conn->net->m.stream_write(conn, p, to_be_sent + MYSQLND_HEADER_SIZE TSRMLS_CC);
RESTORE_HEADER_SIZE(p, safe_storage);
net->compressed_envelope_packet_no++;
}
net->packet_no++;
- p += MYSQLND_MAX_PACKET_SIZE;
- left -= MYSQLND_MAX_PACKET_SIZE;
-
+ p += to_be_sent;
+ left -= to_be_sent;
packets_sent++;
- }
+ /*
+ if left is 0 then there is nothing more to send, but if the last packet was exactly
+ with the size MYSQLND_MAX_PACKET_SIZE we need to send additional packet, which has
+ empty payload. Thus if left == 0 we check for to_be_sent being the max size. If it is
+ indeed it then loop once more, then to_be_sent will become 0, left will stay 0. Empty
+ packet will be sent and this loop will end.
+ */
+ } while (ret && (left > 0 || to_be_sent == MYSQLND_MAX_PACKET_SIZE));
DBG_INF_FMT("packet_size=%d packet_no=%d", left, net->packet_no);
/* Even for zero size payload we have to send a packet */
-
-#ifdef MYSQLND_COMPRESSION_ENABLED
- if (net->compressed == TRUE) {
- /* here we need to compress the data and then write it, first comes the compressed header */
- uLong tmp_complen = left;
- size_t payload_size;
- zend_uchar * uncompressed_payload = p; /* should include the header */
- int res = Z_BUF_ERROR;
-
- STORE_HEADER_SIZE(safe_storage, uncompressed_payload);
- int3store(uncompressed_payload, left);
- int1store(uncompressed_payload + 3, net->packet_no);
- if ((left + MYSQLND_HEADER_SIZE) > MYSQLND_MIN_COMPRESS_LEN) {
- DBG_INF_FMT("compress(%p, %p, %p, %d)", (compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, p, left + MYSQLND_HEADER_SIZE);
- res = compress((compress_buf + COMPRESSED_HEADER_SIZE + MYSQLND_HEADER_SIZE), &tmp_complen, uncompressed_payload, left + MYSQLND_HEADER_SIZE);
- if (res == Z_OK) {
- DBG_INF_FMT("compression successful. compressed size=%d", tmp_complen);
- int3store(compress_buf + MYSQLND_HEADER_SIZE, left + MYSQLND_HEADER_SIZE);
- payload_size = tmp_complen;
- }
- } else {
- DBG_INF("Too short for compression");
- }
- if (res != Z_OK) {
- DBG_INF_FMT("compression NOT successful. error=%d Z_OK=%d Z_BUF_ERROR=%d Z_MEM_ERROR=%d", res, Z_OK, Z_BUF_ERROR, Z_MEM_ERROR);
- int3store(compress_buf + MYSQLND_HEADER_SIZE, 0);
- memcpy(compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, uncompressed_payload, left + MYSQLND_HEADER_SIZE);
- payload_size = left + MYSQLND_HEADER_SIZE;
- }
- RESTORE_HEADER_SIZE(uncompressed_payload, safe_storage);
-
- int3store(compress_buf, payload_size);
- int1store(compress_buf + 3, net->packet_no);
- DBG_INF_FMT("writing %d bytes to the network", payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE);
- ret = conn->net->m.stream_write(conn, compress_buf, payload_size + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE TSRMLS_CC);
-
- net->compressed_envelope_packet_no++;
- #if WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY
- if (res == Z_OK) {
- size_t decompressed_size = left + MYSQLND_HEADER_SIZE;
- uLongf tmp_complen2 = decompressed_size;
- zend_uchar * decompressed_data = malloc(decompressed_size);
- int error = uncompress(decompressed_data, &tmp_complen2, compress_buf + MYSQLND_HEADER_SIZE + COMPRESSED_HEADER_SIZE, payload_size);
- if (error == Z_OK) {
- int i;
- DBG_INF("success decompressing");
- for (i = 0 ; i < decompressed_size; i++) {
- if (i && (i % 30 == 0)) {
- printf("\n\t\t");
- }
- printf("%.2X ", (int)*((char*)&(decompressed_data[i])));
- DBG_INF_FMT("%.2X ", (int)*((char*)&(decompressed_data[i])));
- }
- } else {
- DBG_INF("error decompressing");
- }
- free(decompressed_data);
- }
- #endif /* WHEN_WE_NEED_TO_CHECK_WHETHER_COMPRESSION_WORKS_CORRECTLY */
-
- } else
-#endif /* MYSQLND_COMPRESSION_ENABLED */
- {
- DBG_INF("no compression");
- STORE_HEADER_SIZE(safe_storage, p);
- int3store(p, left);
- int1store(p + 3, net->packet_no);
- ret = conn->net->m.stream_write(conn, p, left + MYSQLND_HEADER_SIZE TSRMLS_CC);
- RESTORE_HEADER_SIZE(p, safe_storage);
- }
- net->packet_no++;
-
if (!ret) {
DBG_ERR_FMT("Can't %u send bytes", count);
conn->state = CONN_QUIT_SENT;