Bug #37044 Read overflow in opt_range.cc found during "make test"
Submitted: 28 May 2008 17:50 Modified: 15 Sep 2009 20:18
Reporter: Michael Zhivich Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:5.0.51a, 5.0.77, 5.0, 5.1, azalea bzr OS:Linux (kernel 2.6.18, gcc 4.0.1)
Assigned to: Georgi Kodinov CPU Architecture:Any

[28 May 2008 17:50] Michael Zhivich
Description:
While testing a research buffer overflow detection tool, I encountered a buffer overflow in MySQL code.

The overflow occurs in get_constant_key_infix() in opt_range.cc, line 8212.  The source argument to memcpy() (cur_range->min_value) points to is_null_string (defined in opt_range.cc, line 59), which is a static array of 2 bytes.  The length argument to memcpy() (field_length), however, is set to 17, thus resulting in an overflow.

I suspect that the logic in the if condition guarding this copy is faulty.  In particular, the 1st part of the || succeeds (that is, (cur_range->maybe_null && cur_range->min_value[0] && cur_range->max_value[0]) evaluates to true); however, the field_length argument is too large for the copy.

How to repeat:
Using the source tarball for MySQL 5.0.51a:

$ ./configure --with-debug
$ make
$ cd mysql-test
$ ./mysql-test-run --gdb group_min_max

Set breakpoint on opt_range.cc:8212 when field_length==17. 
Print out values of cur_range->min_value and &is_null_string to see that the source argument to memcpy() points to a 2-byte static array.

You may be unable to do the above as g++ helpfully inlines get_constant_key_infix() routine, and field_length disappears.
I've found that the following works (of course, your addresses will vary):
(gdb) break opt_range.cc:8212
(gdb) c
(gdb) p &is_null_string
$1=0x0851cdc0
(gdb) break memcpy
(gdb) c
(gdb) si 5 
(this executes instructions that load arguments into: $edi <- dest, $esi <- src, $ecx <- n) 
(gdb) break *$eip if $esi==0x0851cdc0
(gdb) delete 2
(gdb) delete 3
(gdb) c
(gdb) p $ecx
$2=17
(gdb) x/s $esi
0x851cdc0 <is_null_string>: "\001"
[13 Mar 2009 8:27] Sveta Smirnova
Thank you for the report.

I can not repeat the problem with current development sources, although with version 5.0.51a it is repeatable for me. Please try sources from bzr of current version 5.0.77 in your environment to be sure I tested correctly and problem is not repeatable anymore.
[17 Mar 2009 15:07] Michael Zhivich
I was able to replicate the problem with 5.0.77 as well, using a slightly different setup - 64-bit Linux machine with Ubuntu 8.04 (kernel 2.6.24) and gcc 4.2.4.

How to replicate:
- make a change to sql/opt_range.cc, line 8309 to print a message when cur_range->min_value == &is_null_string
- break on opt_range.cc:8310 when running the group_min_max test with --gdb

Output of my gdb session is below:

(gdb) break opt_range.cc:8310
Breakpoint 2 at 0x62d730: file opt_range.cc, line 8310.
Continuing.

Breakpoint 2, get_best_group_min_max (param=0x41543070, tree=0xc67da8)
    at opt_range.cc:8311
8311	            fprintf(stderr, "memcpy: dest=%p, src=%p, length=%d, &is_null_string=%p\n", key_ptr, cur_range->min_value, field_length, &is_null_string);
(gdb) list
8306	        ||
8307	        (memcmp(cur_range->min_value, cur_range->max_value, field_length) == 0))
8308	    { /* cur_range specifies 'IS NULL' or an equality condition. */
8309	        if ((char *)cur_range->min_value == (char *)&is_null_string)
8310	        {
8311	            fprintf(stderr, "memcpy: dest=%p, src=%p, length=%d, &is_null_string=%p\n", key_ptr, cur_range->min_value, field_length, &is_null_string);
8312	        }
8313	      memcpy(key_ptr, cur_range->min_value, field_length);
8314	      key_ptr+= field_length;
8315	      *key_infix_len+= field_length;
(gdb) n
memcpy: dest=0x411bb380, src=0xbbd9e0, length=17, &is_null_string=0xbbd9e0
8313	      memcpy(key_ptr, cur_range->min_value, field_length);
(gdb) bt
#0  get_best_group_min_max (param=0x41543070, tree=0xc67da8)
    at opt_range.cc:8313
#1  0x000000000062f473 in SQL_SELECT::test_quick_select (this=0xc729b8, 
    thd=0xc26980, keys_to_use=<value optimized out>, 
    prev_tables=<value optimized out>, limit=<value optimized out>, 
    force_quick_range=<value optimized out>) at opt_range.cc:2166
#2  0x00000000005cca01 in make_join_statistics (join=0xc71490, 
    tables=<value optimized out>, conds=<value optimized out>, 
    keyuse_array=0xc72670) at sql_select.cc:2348
#3  0x00000000005cdddd in JOIN::optimize (this=0xc71490) at sql_select.cc:913
#4  0x00000000005d920a in mysql_select (thd=0xc26980, 
    rref_pointer_array=0xc28430, tables=0xc65d80, wild_num=0, 
    fields=@0xc28300, conds=0xc664d0, og_num=1, order=0x0, group=0xc666e0, 
    having=0x0, proc_param=0x0, select_options=2156153348, result=0xc667a8, 
    unit=0xc27e60, select_lex=0xc28208) at sql_select.cc:2285
#5  0x00000000005d957f in mysql_explain_union (thd=0xc26980, unit=0xc27e60, 
    result=0xc667a8) at sql_select.cc:15592
#6  0x000000000057b0da in mysql_execute_command (thd=0xc26980)
    at sql_parse.cc:2810
#7  0x0000000000580da7 in mysql_parse (thd=0xc26980, 
    inBuf=0xc656d0 "explain select a1,a2,b,min(c) from t2 where (a2 = 'a') and b is NULL group by a1", length=80, found_semicolon=0x41546b88)
    at sql_parse.cc:6321
#8  0x00000000005819a0 in dispatch_command (command=COM_QUERY, thd=0xc26980, 
    packet=0xc5d6a1 "", packet_length=<value optimized out>)
    at sql_parse.cc:1961
#9  0x0000000000583575 in handle_one_connection (arg=<value optimized out>)
    at sql_parse.cc:1642
#10 0x00007fc56e7793f7 in start_thread () from /lib/libpthread.so.0
#11 0x00007fc56d8fdb3d in clone () from /lib/libc.so.6
#12 0x0000000000000000 in ?? ()
(gdb) q
The program is running.  Exit anyway? (y or n)
[17 Aug 2009 8:58] Sveta Smirnova
Thank you for the feedback.

Verified as described.

Also fails if run in valgrind mode
[24 Aug 2009 12:28] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/81414

2792 Georgi Kodinov	2009-08-24
      Bug #37044: Read overflow in opt_range.cc found during "make test"
      
      The code was using a special global buffer for the value of IS NULL ranges.
      This was not always long enough to be copied by a regular memcpy. As a 
      result read buffer overflows may occur.
      Fixed by setting the null byte to 1 and setting the rest of the field disk image
      to NULL with a bzero (instead of relying on the buffer and memcpy()).
[27 Aug 2009 7:28] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/81664

2795 Georgi Kodinov	2009-08-24
      Bug #37044: Read overflow in opt_range.cc found during "make test"
      
      The code was using a special global buffer for the value of IS NULL ranges.
      This was not always long enough to be copied by a regular memcpy. As a 
      result read buffer overflows may occur.
      Fixed by setting the null byte to 1 and setting the rest of the field disk image
      to NULL with a bzero (instead of relying on the buffer and memcpy()).
[2 Sep 2009 10:25] Bugs System
Pushed into 5.0.86 (revid:joro@sun.com-20090902102337-n5rw8227wwp5cpx8) (version source revid:joro@sun.com-20090824122803-1d5jlaysjc7a7j6q) (merge vers: 5.0.86) (pib:11)
[2 Sep 2009 16:42] Bugs System
Pushed into 5.1.39 (revid:joro@sun.com-20090902154533-8actmfcsjfqovgsb) (version source revid:joro@sun.com-20090827074635-lcg80n5ee98d9y8j) (merge vers: 5.1.39) (pib:11)
[14 Sep 2009 16:03] Bugs System
Pushed into 5.4.4-alpha (revid:alik@sun.com-20090914155317-m1g9wodmndzdj4l1) (version source revid:alik@sun.com-20090914155317-m1g9wodmndzdj4l1) (merge vers: 5.4.4-alpha) (pib:11)
[15 Sep 2009 20:18] Paul DuBois
Noted in 5.0.86, 5.1.39, 5.4.4 changelogs.

A buffer overflow could occur during handling of IS NULL ranges.
[1 Oct 2009 5:58] Bugs System
Pushed into 5.1.39-ndb-6.3.28 (revid:jonas@mysql.com-20091001055605-ap2kiaarr7p40mmv) (version source revid:jonas@mysql.com-20091001055605-ap2kiaarr7p40mmv) (merge vers: 5.1.39-ndb-6.3.28) (pib:11)
[1 Oct 2009 7:25] Bugs System
Pushed into 5.1.39-ndb-7.0.9 (revid:jonas@mysql.com-20091001072547-kv17uu06hfjhgjay) (version source revid:jonas@mysql.com-20091001071652-irejtnumzbpsbgk2) (merge vers: 5.1.39-ndb-7.0.9) (pib:11)
[1 Oct 2009 13:25] Bugs System
Pushed into 5.1.39-ndb-7.1.0 (revid:jonas@mysql.com-20091001123013-g9ob2tsyctpw6zs0) (version source revid:jonas@mysql.com-20091001123013-g9ob2tsyctpw6zs0) (merge vers: 5.1.39-ndb-7.1.0) (pib:11)
[5 Oct 2009 10:50] Bugs System
Pushed into 5.1.39-ndb-6.2.19 (revid:jonas@mysql.com-20091005103850-dwij2dojwpvf5hi6) (version source revid:jonas@mysql.com-20090930185117-bhud4ek1y0hsj1nv) (merge vers: 5.1.39-ndb-6.2.19) (pib:11)
[8 Oct 2009 20:33] Paul DuBois
The 5.4 fix has been pushed to 5.4.3.