Bug #34472 Buffer overrun condition in strmake()
Submitted: 11 Feb 2008 20:51 Modified: 13 Feb 2008 16:44
Reporter: Chuck Bell Email Updates:
Status: Can't repeat Impact on me:
Category:MySQL Server: General Severity:S3 (Non-critical)
Version:6.0.5 OS:Any
Assigned to: Chuck Bell CPU Architecture:Any

[11 Feb 2008 20:51] Chuck Bell
The code in the strmake() method in ./strings/strmake.c has a logic error. This code is guarded by the EXTRA_DEBUG flag. When compiled using this flag, the code writes past the end of the character array clobbering the memory following it. The next memory location after this array is the m_sql_errno in sql_class.h. See the Diagnostics_area class definition below.

Observations/Example Failure
The overrun causes the client to retrieve an incorrect error number for the latest error. This comes from m_sql_errno. Tests that use "--error ER_XYZ" fail with "wrong error" messages. The test suite reports errors like the following.

main.alias                     [ fail ]

mysqltest: At line 107: query 'select t1.* as 'with_alias' from t1' failed with
wrong errno 1114: 'You have an error in your SQL syntax; check the manual that c
orresponds to your MySQL server version for the right syntax to use near 'as 'wi
th_alias' from t1' at line 1', instead of 1064...

The result from queries just before the failure was:
< snip >
AUFNR varchar(12) NOT NULL default '',
PLNFL varchar(6) NOT NULL default '',
VORNR varchar(4) NOT NULL default '',
xstatus_vor smallint(5) unsigned NOT NULL default '0'
INSERT INTO t1 VALUES ('40004712','000001','0010',9);
INSERT INTO t1 VALUES ('40004712','000001','0020',0);
UPDATE t1 SET t1.xstatus_vor = Greatest(t1.xstatus_vor,1) WHERE t1.aufnr =
"40004712" AND t1.plnfl = "000001" AND t1.vornr > "0010" ORDER BY t1.vornr
drop table t1;
drop table if exists t1,t2,t3;
create table t1 (a int, b int, c int);
create table t2 (d int);
create table t3 (a1 int, b1 int, c1 int);
insert into t1 values(1,2,3);
insert into t1 values(11,22,33);
insert into t2 values(99);
select t1.* as 'with_alias' from t1;
ERROR HY000: You have an error in your SQL syntax; check the manual that corresp
onds to your MySQL server version for the right syntax to use near 'as 'with_ali
as' from t1' at line 1

More results from queries before failure can be found in d:\source\c++\mysql-6.0

Aborting: main.alias failed in default mode.
To continue, re-run with '--force'.
Stopping All Servers

Here is the problem (see ./strings/strmake.c):

0044  uint n= strlen(src) + 1;
0045  if (n <= length)
0046    memset(dst + n, (int) 'Z', length - n + 1);

Given: src = 'test'
       length = 10
       dst is some valid memory location

after 0044, n = 5
when 0046 is executed, memset will copy (10 - 5 + 1 = 6) Z's starting at position 5

dst[0]  = ?? (used later)
dst[1]  = ?? (used later)
dst[2]  = ?? (used later)
dst[3]  = ?? (used later)
dst[4]  = ?? (used later)
dst[5]  = Z  that's 1...
dst[6]  = Z  that's 2...
dst[7]  = Z  that's 3...
dst[8]  = Z  that's 4...
dst[9]  = Z  that's 5...
dst[10] = Z  oh, my!  <-- writes a 'Z' in part of the m_sql_errno memory!

class Diagnostics_area
  /** Message buffer. Can be used by OK or ERROR status. */
  char m_message[MYSQL_ERRMSG_SIZE];
    SQL error number. One of ER_ codes from share/errmsg.txt.
    Set by set_error_status.
  uint m_sql_errno;

* The EXTRA_DEBUG flag is used in the online backup code.
* See mysql-6.0-backup/win for modifications to enable this flag on Windows.
  (can also be enabled manually in Visual Studio).
* This bug does not appear on Linux despite compiling with EXTRA_DEBUG. It
  is on the Windows platform (have tested XP and Vista) that the problem
* This code is in the mysql-6.0 tree.

How to repeat:
clone mysql-6.0
build on Windows using EXTRA_DEBUG
attempt to run any test that uses "--error ER_XXX" e.g. alias:
./mysql-test-run.pl alias

Witness the mayhem!

Suggested fix:
A patch has been proposed to correct the buffer overrun:
[11 Feb 2008 20:53] Chuck Bell
Oops. Here is the proposed patch.

===== strmake.c 1.11 vs edited =====
--- 1.11/strings/strmake.c      2007-12-17 04:43:18 -05:00
+++ edited/strmake.c    2008-02-11 14:10:40 -05:00
@@ -43,7 +43,7 @@
   uint n= strlen(src) + 1;
   if (n <= length)
-    memset(dst + n, (int) 'Z', length - n + 1);
+    memset(dst + n, (int) 'Z', length - n);

   while (length--)
[12 Feb 2008 17:59] Chuck Bell
Patch ready for review. See:

[13 Feb 2008 16:43] Chuck Bell
This bug was fixed in patch 1.375 to mysql-6.0 tree submitted by kostja on 09 February.