Bug #19216: Client crashes on long SELECT

 The server sends a number of columns to the client.
 It uses a limited "fast" function for that instead of the
 general one. This fast function cannot send numbers larger 
 than 2 bytes. 
 This causes the client to expect smaller number of columns. 
 The client writes outside of the allocated memory buffer 
 as a result.
 Fixed the server to use the general function to send column
 count.
 Fixed the client to check the column count before writing
 column data. 
parent a7713641
...@@ -33,3 +33,21 @@ ...@@ -33,3 +33,21 @@
# #
--exec echo 'help' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp --exec echo 'help' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp
--exec echo 'help ' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp --exec echo 'help ' | $MYSQL > $MYSQLTEST_VARDIR/tmp/bug20328.tmp
#
# Bug #19216: Client crashes on long SELECT
#
--exec echo "select" > $MYSQLTEST_VARDIR/tmp/b19216.tmp
# 3400 * 20 makes 68000 columns that is more than the max number that can fit
# in a 16 bit number.
let $i= 3400;
while ($i)
{
--exec echo "'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a'," >> $MYSQLTEST_VARDIR/tmp/b19216.tmp
dec $i;
}
--exec echo "'b';" >> $MYSQLTEST_VARDIR/tmp/b19216.tmp
--disable_query_log
--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/b19216.tmp >/dev/null
--enable_query_log
...@@ -1173,6 +1173,8 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, ...@@ -1173,6 +1173,8 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
for (row=data->data; row ; row = row->next,field++) for (row=data->data; row ; row = row->next,field++)
{ {
uchar *pos; uchar *pos;
/* fields count may be wrong */
DBUG_ASSERT ((field - result) < fields);
cli_fetch_lengths(&lengths[0], row->data, default_value ? 8 : 7); cli_fetch_lengths(&lengths[0], row->data, default_value ? 8 : 7);
field->catalog = strdup_root(alloc,(char*) row->data[0]); field->catalog = strdup_root(alloc,(char*) row->data[0]);
field->db = strdup_root(alloc,(char*) row->data[1]); field->db = strdup_root(alloc,(char*) row->data[1]);
......
...@@ -43,7 +43,7 @@ bool Protocol_prep::net_store_data(const char *from, uint length) ...@@ -43,7 +43,7 @@ bool Protocol_prep::net_store_data(const char *from, uint length)
packet->realloc(packet_length+9+length)) packet->realloc(packet_length+9+length))
return 1; return 1;
char *to=(char*) net_store_length((char*) packet->ptr()+packet_length, char *to=(char*) net_store_length((char*) packet->ptr()+packet_length,
(ulonglong) length); length);
memcpy(to,from,length); memcpy(to,from,length);
packet->length((uint) (to+length-packet->ptr())); packet->length((uint) (to+length-packet->ptr()));
return 0; return 0;
...@@ -297,8 +297,8 @@ send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message) ...@@ -297,8 +297,8 @@ send_ok(THD *thd, ha_rows affected_rows, ulonglong id, const char *message)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
buff[0]=0; // No fields buff[0]=0; // No fields
pos=net_store_length(buff+1,(ulonglong) affected_rows); pos=net_store_length(buff+1,affected_rows);
pos=net_store_length(pos, (ulonglong) id); pos=net_store_length(pos, id);
if (thd->client_capabilities & CLIENT_PROTOCOL_41) if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{ {
DBUG_PRINT("info", DBUG_PRINT("info",
...@@ -416,7 +416,7 @@ bool send_old_password_request(THD *thd) ...@@ -416,7 +416,7 @@ bool send_old_password_request(THD *thd)
ulonglong for bigger numbers. ulonglong for bigger numbers.
*/ */
char *net_store_length(char *pkg, uint length) static char *net_store_length_fast(char *pkg, uint length)
{ {
uchar *packet=(uchar*) pkg; uchar *packet=(uchar*) pkg;
if (length < 251) if (length < 251)
...@@ -439,7 +439,7 @@ char *net_store_length(char *pkg, uint length) ...@@ -439,7 +439,7 @@ char *net_store_length(char *pkg, uint length)
char *net_store_data(char *to,const char *from, uint length) char *net_store_data(char *to,const char *from, uint length)
{ {
to=net_store_length(to,length); to=net_store_length_fast(to,length);
memcpy(to,from,length); memcpy(to,from,length);
return to+length; return to+length;
} }
...@@ -448,7 +448,7 @@ char *net_store_data(char *to,int32 from) ...@@ -448,7 +448,7 @@ char *net_store_data(char *to,int32 from)
{ {
char buff[20]; char buff[20];
uint length=(uint) (int10_to_str(from,buff,10)-buff); uint length=(uint) (int10_to_str(from,buff,10)-buff);
to=net_store_length(to,length); to=net_store_length_fast(to,length);
memcpy(to,buff,length); memcpy(to,buff,length);
return to+length; return to+length;
} }
...@@ -457,7 +457,7 @@ char *net_store_data(char *to,longlong from) ...@@ -457,7 +457,7 @@ char *net_store_data(char *to,longlong from)
{ {
char buff[22]; char buff[22];
uint length=(uint) (longlong10_to_str(from,buff,10)-buff); uint length=(uint) (longlong10_to_str(from,buff,10)-buff);
to=net_store_length(to,length); to=net_store_length_fast(to,length);
memcpy(to,buff,length); memcpy(to,buff,length);
return to+length; return to+length;
} }
...@@ -520,7 +520,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag) ...@@ -520,7 +520,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
if (flag & 1) if (flag & 1)
{ // Packet with number of elements { // Packet with number of elements
char *pos=net_store_length(buff, (uint) list->elements); char *pos=net_store_length(buff, list->elements);
(void) my_net_write(&thd->net, buff,(uint) (pos-buff)); (void) my_net_write(&thd->net, buff,(uint) (pos-buff));
} }
...@@ -648,7 +648,7 @@ bool Protocol::send_records_num(List<Item> *list, ulonglong records) ...@@ -648,7 +648,7 @@ bool Protocol::send_records_num(List<Item> *list, ulonglong records)
{ {
char *pos; char *pos;
char buff[20]; char buff[20];
pos=net_store_length(buff, (uint) list->elements); pos=net_store_length(buff, list->elements);
pos=net_store_length(pos, records); pos=net_store_length(pos, records);
return my_net_write(&thd->net, buff,(uint) (pos-buff)); return my_net_write(&thd->net, buff,(uint) (pos-buff));
} }
......
...@@ -177,7 +177,6 @@ void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, ...@@ -177,7 +177,6 @@ void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L,
const char *info=0); const char *info=0);
void send_eof(THD *thd, bool no_flush=0); void send_eof(THD *thd, bool no_flush=0);
bool send_old_password_request(THD *thd); bool send_old_password_request(THD *thd);
char *net_store_length(char *packet,uint length);
char *net_store_data(char *to,const char *from, uint length); char *net_store_data(char *to,const char *from, uint length);
char *net_store_data(char *to,int32 from); char *net_store_data(char *to,int32 from);
char *net_store_data(char *to,longlong from); char *net_store_data(char *to,longlong from);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment