Bug #42803 Field_bit does not have unsigned_flag field, can lead to bad memory access
Submitted: 12 Feb 2009 19:11 Modified: 18 Dec 2009 13:21
Reporter: Gene Pang Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:5.0.37, 4.1, 5.0, 5.1, 6.0 bzr OS:Any
Assigned to: Ramil Kalimullin CPU Architecture:Any
Tags: Contribution, Field_bit, unsigned_flag

[12 Feb 2009 19:11] Gene Pang
Description:
There is a possible incorrect memory access in opt_range.cc, get_mm_leaf().

In this code fragment, ((Field_num*)field)->unsigned_flag may not always work, since field could be Field_bit, and Field_bit does NOT have the unsigned_flag field.

Field_bit is the only class where result_type() is INT_RESULT, but does NOT have unsigned_flag, from Field_num.

  /*
    Check if we are comparing an UNSIGNED integer with a negative constant.
    In this case we know that:
    (a) (unsigned_int [< | <=] negative_constant) == FALSE
    (b) (unsigned_int [> | >=] negative_constant) == TRUE
    In case (a) the condition is false for all values, and in case (b) it
    is true for all values, so we can avoid unnecessary retrieval and condition
    testing, and we also get correct comparison of unsinged integers with
    negative integers (which otherwise fails because at query execution time
    negative integers are cast to unsigned if compared with unsigned).
   */
  if (field->result_type() == INT_RESULT &&
      value->result_type() == INT_RESULT &&
      ((Field_num*)field)->unsigned_flag && !((Item_int*)value)->unsigned_flag)
  {
    ...
  }

How to repeat:
I ran type_bit test with --valgrind.

A smaller test case:

create table t1 (a bit(7), b bit(9), key(a, b));
insert into t1 values 
(94, 46), (31, 438), (61, 152), (78, 123), (88, 411), (122, 118), (0, 177),    
(75, 42), (108, 67), (79, 349), (59, 188), (68, 206), (49, 345), (118, 380),   
(111, 368), (94, 468), (56, 379), (77, 133), (29, 399), (9, 363), (23, 36),    
(116, 390), (119, 368), (87, 351), (123, 411), (24, 398), (34, 202), (28, 499),
(30, 83), (5, 178), (60, 343), (4, 245), (104, 280), (106, 446), (127, 403),   
(44, 307), (68, 454), (57, 135);
explain select a+0, b+0 from t1 where a > 40 and a < 70 order by 2;

Suggested fix:
Checking for (field->type() == FIELD_TYPE_BIT) before casting it to Field_num would solve it, since Field_bit is always unsigned.

  if (field->result_type() == INT_RESULT &&
      value->result_type() == INT_RESULT &&
      ((field->type() == FIELD_TYPE_BIT) ||
       ((Field_num*)field)->unsigned_flag) &&
      !((Item_int*)value)->unsigned_flag)
  {
    ...
  }
[14 Feb 2009 12:41] Sveta Smirnova
Thank you for the report.

Memory leak verified with following valgrind output (version 6.0):

CURRENT_TEST: main.bug42803
==7364== Memcheck, a memory error detector.
==7364== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==7364== Using LibVEX rev 1854, a library for dynamic binary translation.
==7364== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==7364== Using valgrind-3.3.1, a dynamic binary instrumentation framework.
==7364== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==7364== For more details, rerun with: -v
==7364== 
090214 15:35:04 [Warning] The syntax '--log' is deprecated and will be removed in MySQL 7.0. Please use '--general-log --general-log-file' instead.
090214 15:35:05 [Warning] The syntax '--log-slow-queries' is deprecated and will be removed in MySQL 7.0. Please use '--slow-query-log'/'--slow-query-log-file' instead.
090214 15:35:05 [Note] Plugin 'InnoDB' disabled by command line option
090214 15:35:07 [Note] Falcon: unable to open system data files.
090214 15:35:07 [Note] Falcon: creating new system data files.
090214 15:35:13 [Note] Event Scheduler: Loaded 0 events
090214 15:35:13 [Note] /users/ssmirnova/build/mysql-6.0/libexec/mysqld: ready for connections.
Version: '6.0.10-alpha-debug-log'  socket: '/users/ssmirnova/build/mysql-6.0/mysql-test/var/tmp/mysqld.1.sock'  port: 12500  Source distribution
==7364== Thread 15:
==7364== Syscall param write(buf) points to uninitialised byte(s)
==7364==    at 0x46419B: (within /lib/libpthread-2.3.6.so)
==7364==    by 0x87ECDEE: my_b_flush_io_cache (mf_iocache.c:1771)
==7364==    by 0x87ED199: end_io_cache (mf_iocache.c:1859)
==7364==    by 0x84F979D: mi_extra (mi_extra.c:171)
==7364==    by 0x84EADC9: ha_myisam::end_bulk_insert(bool) (ha_myisam.cc:1399)
==7364==    by 0x8376A5C: handler::ha_end_bulk_insert(bool) (handler.h:1609)
==7364==    by 0x83715F0: mysql_insert(THD*, TABLE_LIST*, List<Item>&, List<List<Item> >&, List<Item>&, List<Item>&, enum_duplicates, bool) (sql_insert.cc:866)
==7364==    by 0x82D20C1: mysql_execute_command(THD*) (sql_parse.cc:3122)
==7364==    by 0x82D80B4: mysql_parse(THD*, char const*, unsigned, char const**) (sql_parse.cc:5751)
==7364==    by 0x82D8AF6: dispatch_command(enum_server_command, THD*, char*, unsigned) (sql_parse.cc:1009)
==7364==    by 0x82D9E5A: do_command(THD*) (sql_parse.cc:691)
==7364==    by 0x82C7D6A: handle_one_connection (sql_connect.cc:1146)
==7364==    by 0x45FBD3: start_thread (in /lib/libpthread-2.3.6.so)
==7364==    by 0x3B74FD: clone (in /lib/libc-2.3.6.so)
==7364==  Address 0x7fab5a9 is 25 bytes inside a block of size 131,100 alloc'd
==7364==    at 0x40047ED: malloc (vg_replace_malloc.c:207)
==7364==    by 0x87EFE29: _mymalloc (safemalloc.c:137)
==7364==    by 0x87E9F16: init_io_cache (mf_iocache.c:238)
==7364==    by 0x84F96F0: mi_extra (mi_extra.c:139)
==7364==    by 0x84EAC04: ha_myisam::start_bulk_insert(unsigned long long) (ha_myisam.cc:1357)
==7364==    by 0x8376A20: handler::ha_start_bulk_insert(unsigned long long) (handler.h:1604)
==7364==    by 0x83711B4: mysql_insert(THD*, TABLE_LIST*, List<Item>&, List<List<Item> >&, List<Item>&, List<Item>&, enum_duplicates, bool) (sql_insert.cc:751)
==7364==    by 0x82D20C1: mysql_execute_command(THD*) (sql_parse.cc:3122)
==7364==    by 0x82D80B4: mysql_parse(THD*, char const*, unsigned, char const**) (sql_parse.cc:5751)
==7364==    by 0x82D8AF6: dispatch_command(enum_server_command, THD*, char*, unsigned) (sql_parse.cc:1009)
==7364==    by 0x82D9E5A: do_command(THD*) (sql_parse.cc:691)
==7364==    by 0x82C7D6A: handle_one_connection (sql_connect.cc:1146)
==7364==    by 0x45FBD3: start_thread (in /lib/libpthread-2.3.6.so)
==7364==    by 0x3B74FD: clone (in /lib/libc-2.3.6.so)
==7364== 
==7364== Syscall param pwrite64(buf) points to uninitialised byte(s)
==7364==    at 0x464EA7: pwrite64 (in /lib/libpthread-2.3.6.so)
==7364==    by 0x87E146C: my_pwrite (my_pread.c:162)
==7364==    by 0x87E2CEC: key_cache_pwrite (mf_keycache.c:796)
==7364==    by 0x87E8473: flush_cached_blocks (mf_keycache.c:3540)
==7364==    by 0x87E8B23: flush_key_blocks_int (mf_keycache.c:3836)
==7364==    by 0x87E9069: flush_key_blocks (mf_keycache.c:4093)
==7364==    by 0x85037F0: mi_lock_database (mi_locking.c:72)
==7364==    by 0x84EBF33: ha_myisam::external_lock(THD*, int) (ha_myisam.cc:1752)
==7364==    by 0x8407297: handler::ha_external_lock(THD*, int) (handler.cc:5400)
==7364==    by 0x82B2DA0: unlock_external(THD*, TABLE**, unsigned) (lock.cc:823)
==7364==    by 0x82B2E57: mysql_unlock_tables(THD*, st_mysql_lock*) (lock.cc:438)
==7364==    by 0x831AA12: close_thread_tables(THD*, bool) (sql_base.cc:1459)
==7364==    by 0x82D99F7: dispatch_command(enum_server_command, THD*, char*, unsigned) (sql_parse.cc:1410)
==7364==    by 0x82D9E5A: do_command(THD*) (sql_parse.cc:691)
==7364==    by 0x82C7D6A: handle_one_connection (sql_connect.cc:1146)
==7364==    by 0x45FBD3: start_thread (in /lib/libpthread-2.3.6.so)
==7364==  Address 0x412b9ac is 3,516 bytes inside a block of size 923,676 alloc'd
==7364==    at 0x40047ED: malloc (vg_replace_malloc.c:207)
==7364==    by 0x87EFE29: _mymalloc (safemalloc.c:137)
==7364==    by 0x880DF8A: my_large_malloc (my_largepage.c:64)
==7364==    by 0x87E22DB: init_key_cache (mf_keycache.c:438)
==7364==    by 0x840611C: ha_init_key_cache (handler.cc:3640)
==7364==    by 0x82E1FB0: process_key_caches(int (*)(char const*, st_key_cache*)) (set_var.cc:4499)
==7364==    by 0x82BE78C: init_server_components() (mysqld.cc:4126)
==7364==    by 0x82C2940: main (mysqld.cc:4643)
[8 Oct 2009 11:57] 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/86142

2815 Ramil Kalimullin	2009-10-08
      Fix for bug #42803: Field_bit does not have unsigned_flag field,
      can lead to bad memory access
      
      Problem: Field_bit is the only field which returns INT_RESULT
      and doesn't have unsigned flag. As it's not a descendant of the 
      Field_num, so using ((Field_num *) field_bit)->unsigned_flag may lead
      to unpredictable results.
      
      Fix: check the field type before casting.
     @ mysql-test/r/type_bit.result
        Fix for bug #42803: Field_bit does not have unsigned_flag field,
        can lead to bad memory access
          - test result.
     @ mysql-test/t/type_bit.test
        Fix for bug #42803: Field_bit does not have unsigned_flag field,
        can lead to bad memory access
          - test case.
     @ sql/opt_range.cc
        Fix for bug #42803: Field_bit does not have unsigned_flag field,
        can lead to bad memory access
          - don't cast to (Field_num *) Field_bit, as it's not a Field_num
        descendant and is always unsigned by nature.
[8 Oct 2009 16:54] Ramil Kalimullin
Pushed to {5.0, 5.1}-bugteam, -pe.
[14 Oct 2009 14:35] Bugs System
Pushed into 5.0.88 (revid:joro@sun.com-20091014143320-kwcqatf2g9cjbx43) (version source revid:ramil@mysql.com-20091008115631-jjozkd7q3g82x3iw) (merge vers: 5.0.87) (pib:13)
[14 Oct 2009 14:39] Bugs System
Pushed into 5.1.41 (revid:joro@sun.com-20091014143611-cphb0enjlx6lpat1) (version source revid:satya.bn@sun.com-20091013071829-zc4c3go44j6re592) (merge vers: 5.1.40) (pib:13)
[15 Oct 2009 23:47] Paul DuBois
Noted in 5.0.88, 5.1.41 changelogs.

Failure to treat BIT values as unsigned could lead to unpredictable
results. 

Setting report to NDI pending push into 5.5.x.+
[22 Oct 2009 6:36] Bugs System
Pushed into 6.0.14-alpha (revid:alik@sun.com-20091022063126-l0qzirh9xyhp0bpc) (version source revid:alik@sun.com-20091019135554-s1pvptt6i750lfhv) (merge vers: 6.0.14-alpha) (pib:13)
[22 Oct 2009 7:08] Bugs System
Pushed into 5.5.0-beta (revid:alik@sun.com-20091022060553-znkmxm0g0gm6ckvw) (version source revid:alik@sun.com-20091019131708-bc6pv55x6287a0wc) (merge vers: 5.5.0-beta) (pib:13)
[22 Oct 2009 19:57] Paul DuBois
Noted in 5.5.0, 6.0.14 changelogs.
[18 Dec 2009 10:35] Bugs System
Pushed into 5.1.41-ndb-7.1.0 (revid:jonas@mysql.com-20091218102229-64tk47xonu3dv6r6) (version source revid:jonas@mysql.com-20091218095730-26gwjidfsdw45dto) (merge vers: 5.1.41-ndb-7.1.0) (pib:15)
[18 Dec 2009 10:51] Bugs System
Pushed into 5.1.41-ndb-6.2.19 (revid:jonas@mysql.com-20091218100224-vtzr0fahhsuhjsmt) (version source revid:jonas@mysql.com-20091217101452-qwzyaig50w74xmye) (merge vers: 5.1.41-ndb-6.2.19) (pib:15)
[18 Dec 2009 11:06] Bugs System
Pushed into 5.1.41-ndb-6.3.31 (revid:jonas@mysql.com-20091218100616-75d9tek96o6ob6k0) (version source revid:jonas@mysql.com-20091217154335-290no45qdins5bwo) (merge vers: 5.1.41-ndb-6.3.31) (pib:15)
[18 Dec 2009 11:20] Bugs System
Pushed into 5.1.41-ndb-7.0.11 (revid:jonas@mysql.com-20091218101303-ga32mrnr15jsa606) (version source revid:jonas@mysql.com-20091218064304-ezreonykd9f4kelk) (merge vers: 5.1.41-ndb-7.0.11) (pib:15)
[18 Dec 2009 13:21] MC Brown
Already noted in earlier changelogs.