| 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: | |
| 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: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

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 // *******************************************************************