Bug #60212 Server crash with corrupt fetch buffer
Submitted: 22 Feb 2011 20:01 Modified: 5 Mar 2015 11:16
Reporter: John Water Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S1 (Critical)
Version:5.5.9 OS:Any
Assigned to: Jimmy Yang CPU Architecture:Any
Tags: nchar, server crash

[22 Feb 2011 20:01] John Water
Description:
The MySQL 5.5.9 server crashes with the following info:

Files that help describe the problem:
  C:\ProgramData\Microsoft\Windows\WER\ReportQueue\AppCrash_mysqld.exe_56ab42f677774c1c261a6fadcafa3312f7920d8_cab_5850b559\WERB3C3.tmp.appcompat.txt
  C:\ProgramData\Microsoft\Windows\WER\ReportQueue\AppCrash_mysqld.exe_56ab42f677774c1c261a6fadcafa3312f7920d8_cab_5850b559\WERB403.tmp.WERInternalMetadata.xml
  C:\ProgramData\Microsoft\Windows\WER\ReportQueue\AppCrash_mysqld.exe_56ab42f677774c1c261a6fadcafa3312f7920d8_cab_5850b559\WERB50E.tmp.mdmp

How to repeat:
Please run the following SQL statements using mysql.exe against MySQL 5.5.9 server, you will see the server crash:
*******************************************************************
delimiter //

drop table test
//
create table test (
    pk	integer		primary key not null,
    c1	varchar(2000) character set ucs2 NULL ) 
//
INSERT INTO test VALUES( 1, 'hello' )
//
INSERT INTO test VALUES( 2, NULL )
//
INSERT INTO test VALUES( 3, 'rat' )
//
INSERT INTO test VALUES( 4, '' )
//
INSERT INTO test VALUES( 5, repeat( 'nvarchar', 250 ) )
//
INSERT INTO test VALUES( 6, 'dummy' )
//
INSERT INTO test VALUES( 7, 'dummy' )
//
INSERT INTO test VALUES( 8, NULL )
//
commit
//
select * from test
//
commit
//
drop table test
//
commit
//
*******************************************************************
[22 Feb 2011 20:55] Peter Laursen
I have some observations:

1) I clean up the script: Using the MySQL default delimiter + removing COMMITs that are irrelevant with MySQL (as there is no BEGIN/START TRANSACTION and I assume AUTOCOMMIT = 1). So after this it looks like: 

DROP TABLE IF EXISTS test;
 CREATE TABLE test (
    pk	INTEGER		PRIMARY KEY NOT NULL,
    c1	VARCHAR(2000) CHARACTER SET ucs2 NULL );
INSERT INTO test VALUES( 1, 'hello' );
INSERT INTO test VALUES( 2, NULL );
INSERT INTO test VALUES( 3, 'rat' );
INSERT INTO test VALUES( 4, '' );
INSERT INTO test VALUES( 5, REPEAT( 'nvarchar', 250 ) );
INSERT INTO test VALUES( 6, 'dummy' );
INSERT INTO test VALUES( 7, 'dummy' );
INSERT INTO test VALUES( 8, NULL );
SELECT * FROM test;

2) with MySQL 5.1.55 I can run run the script from any client multiple times. On 5.5.9 it most often crashes (but not each time - sometimes I will have to run it twice). Changing charset to utf8 does not change anything. But with latin1 charset it does not crash ever.  Also if I use MyISAM it does not. Both

CREATE TABLE test (
    pk	INTEGER		PRIMARY KEY NOT NULL,
    c1	VARCHAR(2000) CHARACTER SET latin1 NULL)

and

CREATE TABLE test (
    pk	INTEGER		PRIMARY KEY NOT NULL,
    c1	VARCHAR(2000) CHARACTER SET ucs2 NULL)
  ENGINE MYISAM;)

.. never crash for me. 

So
1) InnoDB + specific (unicode/multibyte) charsets + 5.5.9 causes this in combination
2) The user defined DELIMITER and the COMMITs in the original test case are not required to reproduce.
[22 Feb 2011 21:26] MySQL Verification Team
Backtrace on Vista X86_64

Attachment: backtrace_60212.txt (text/plain), 5.84 KiB.

[22 Feb 2011 21:50] MySQL Verification Team
Thank you for the bug report.
[23 Feb 2011 7:26] John Embretsen
Issue is not limited to Windows, I see it on Unix as well.
[23 Feb 2011 14:25] Jimmy Yang
This seems to be a regression from bug #52199, which introduced a new function row_mysql_pad_col(), including a "do while" loop to pad column. Unfortunately, do while loop checks the condition after the padding (not before), so in following code, even if len = 0, and pad == pad_end, it will still do padding, thus corrupt the ROW_PREBUILT_FETCH_MAGIC_N marker we put before and after the prebuilt->fetch_cache[] array:

+row_mysql_pad_col(

....
+	case 2:
+		/* space=0x0020 */
+		pad_end = pad + len;
+		ut_a(!(len % 2));
+		do {
+			*pad++ = 0x00;
+			*pad++ = 0x20;
+		} while (pad < pad_end);
+		break;
}

(gdb) p len
$70 = 0
(gdb) 
293				*pad++ = 0x00;
294				*pad++ = 0x20;

The row length is 4007, 
(gdb) p table->s->reclength
$77 = 4007
gdb) p  prebuilt->mysql_row_len
$75 = 4007

so prebuilt->fetch_cache[0] is allocated with
buf = mem_alloc(prebuilt->mysql_row_len + 8);
with 8 bytes for before and after ROW_PREBUILT_FETCH_MAGIC_N marker.

There are two columns, 
1) pk integer is 4 bytes add 1 byte length info
2) varchar(2000) character set ucs2 is 2000 X 2 = 4000 bytes add 2 byte length info.

So we have total 4 + 4000 + 1 + 2 = 4007 bytes, so when it comes to  row_mysql_pad_col() in row_sel_field_store_in_mysql_format(), it wrongfully padded it, thus we eventually had a memory corruption and report it when we free prebuilt->fetch_cache[0]. 

0x90c81fb is our 4007th byte, and its value is ROW_PREBUILT_FETCH_MAGIC_N (0x3705c31b)
(gdb) x 0x90c81fb
0x90c81fb:	0x3705c31b

so we will see after row_mysql_pad_col(), its value changed by padded 0x2000 there.

(gdb) x 0x90c821b
0x90c821b:	0x37052000

Fix would be simply change the do while loop to while loop, and does the check before padding.
[23 Feb 2011 14:56] Jimmy Yang
The row_mysql_pad_col() causing the problem is used in two places,  except for row_sel_field_store_in_mysql_format() in this case, it is also used in row_ins_cascade_calc_update_vec() where there was a do while loop even before bug #52199 fix, however it guarantees the pad_len > 0, so do while loop is fine, and so does current row_mysql_pad_col(). I guess this is where this do while loop originally extracted.

In any case, by simply changing the do while loop in row_mysql_pad_col() to while loop (checks condition first) could solve the root cause and crash problem.
[25 Apr 2011 20:47] John Water
I have checked the MySQL 5.5.11 GA and found this crash problem is still there.  I was wondering when you will fix this problem?
[6 Oct 2011 19:05] John Water
Are you going to fix this server crash bug?
[5 Mar 2015 11:17] MySQL Verification Team
this was fixed in *5.5.20 and 5.6.4*
Bug 13405367 - 60212 SERVER CRASH WITH CORRUPT FETCH BUFFER