Bug #80465 Accessing uninitialized memory sql/sql_table.cc mysql_check_table() line ~10362
Submitted: 22 Feb 2016 4:40 Modified: 5 Sep 12:27
Reporter: Sergey Sprogis Email Updates:
Status: Won't fix Impact on me:
None 
Category:MySQL Server: Compiling Severity:S3 (Non-critical)
Version:5.7.10 OS:Solaris (Sparc, 11.3)
Assigned to: CPU Architecture:Any

[22 Feb 2016 4:40] Sergey Sprogis
Description:
Oracle's tool called Discover detects UMR (Uninitialized Memory Read)
in mysqld server when executing '1st' test case located inside
MySQL 5.7.10. This UMR was produced on Sparc Solaris 11.3 T7 system,
but it's not platform specific, and can occur on any other platform.

Error message and call stack reported by Discover looks like this:
======================================
(UMR): accessing uninitialized data  (1 byte) on the heap:
 mysql_checksum_table(THD*, TABLE_LIST*, st_ha_check_opt*) + 0x2f6c <sql_table.cc : 10362>
  10362:=> t->record[0][t->s->null_bytes-1] |= null_mask;
 mysql_execute_command(THD*, bool) + 0xe2f0 <sql_parse.cc : 3471>
  3471:=> res = mysql_checksum_table(thd, first_table, &lex->check_opt);
 sp_instr_stmt::exec_core(THD*, unsigned int*) + 0x748 <sp_instr.cc : 948>
  948:=>  bool rc= mysql_execute_command(thd);
 sp_lex_instr::reset_lex_and_exec_core(THD*, unsigned int*, bool) + 0x2a9c <sp_instr.cc : 411>
  411:=>  error= exec_core(thd, nextp);
 sp_lex_instr::validate_lex_and_execute_core(THD*, unsigned int*, bool) + 0xc80 <sp_instr.cc : 676>
  676:=>  bool rc= reset_lex_and_exec_core(thd, nextp, open_tables);
 sp_instr_stmt::execute(THD*, unsigned int*) + 0x1268 <sp_instr.cc : 859>
  859:=>  rc= validate_lex_and_execute_core(thd, nextp, false);
 sp_head::execute(THD*, bool) + 0x404c <sp_head.cc : 789>
  789:=>  err_status= i->execute(thd, &ip);
 sp_head::execute_procedure(THD*, List<Item>*) + 0x3924 <sp_head.cc : 1522>
  1522:=> err_status= execute(thd, TRUE);

Was allocated at (1024 bytes) [t@25]:
 my_raw_malloc() + 0x2a4 <my_malloc.c : 191>
  191:=>  point= malloc(size);
 my_malloc() + 0x1a4 <my_malloc.c : 54>
  54:=>   mh= (my_memory_header*) my_raw_malloc(raw_size, flags);
 alloc_root() + 0xea0 <my_alloc.c : 280>
  280:=>  get_size,MYF(MY_WME | ME_FATALERROR))))
 Sql_alloc::operator new(unsigned long, st_mem_root*) + 0x110 <sql_alloc.h : 39>
  39:=>   { return alloc_root(mem_root, size); }
 innobase_create_handler(handlerton*, TABLE_SHARE*, st_mem_root*) + 0x660 <ha_innodb.cc : 1327>
  1327:=> return(new (mem_root) ha_innobase(hton, table));
 get_new_handler(TABLE_SHARE*, st_mem_root*, handlerton*) + 0x4a0 <handler.cc : 652>
  652:=>  if ((file= db_type->create(db_type, share, alloc)))
 open_table_from_share(THD*, TABLE_SHARE*, ... <table.cc : 3050>
  3050:=> share->db_type())))
 open_table(THD*, TABLE_LIST*, Open_table_context*) + 0x5dec <sql_base.cc : 3473>
  3473:=> thd->open_options, table, FALSE);
======================================

How to repeat:
1. If you have access to Discover tool, and latest Sun Studio C/C++ compilers
   you need to build MySQL-5.7.10 sources in debug mode on Solaris Sparc,
   then launch discover binary with input argument as mysqld binary,
   and when completed to take a look inside mysql.html file which should
   contain call stack shown in description above.

2. If you do not have access to Discover, you need to debug MySQL server
   with '1st' test case, and to see that t->record[0][t->s->null_bytes-1] operand  located
   inside sql/sql_table.cc mysql_check_table() line ~10362

  t->record[0][t->s->null_bytes-1] |= null_mask;
  
   is indeed uninitialized before that point.
  
   Below is 33 lines independent executable t.cc test case which accurately
   simulates original issue. For it Discover produces exactly the same UMR
   message as for original mysqld server.

t.cc
===================================
#include <stdlib.h>
typedef  unsigned char uchar;
typedef  unsigned int uint;
struct TABLE_SHARE {
 TABLE_SHARE() {}
 uint null_bytes;
};
struct TABLE {
 TABLE() {}
 TABLE_SHARE *s;
 uchar *record[2];
};
void open_table_from_share(TABLE_SHARE *share,TABLE *outparam) {
 uint records=2;
 uchar *record;
 outparam->s=share;
 record=(uchar*)malloc(496);
 outparam->record[0]=record;
}
void mysql_checksum_table(TABLE *t) {
 uchar null_mask=254;
 t->record[0][t->s->null_bytes-1] |= null_mask;
}
int main () {
 TABLE_SHARE  *pts;
 TABLE *pt;
 pt=(TABLE*)malloc(sizeof(TABLE));
 pts=(TABLE_SHARE*)malloc(sizeof(TABLE_SHARE));
 pts->null_bytes=1;
 open_table_from_share(pts,pt);
 mysql_checksum_table(pt);
 return 0;
}
===================================

Suggested fix:
 Somehow initialize t->record[0][t->s->null_bytes-1]  operand  before  its read in:
t->record[0][t->s->null_bytes-1] |= null_mask;
[22 Feb 2016 12:57] Umesh Shastry
Hello Sergey,

Thank you for the report!

Thanks,
Umesh
[5 Sep 12:27] Erlend Dahl
Posted by developer:

[30 Jul 2019 5:15] Dyre Tjeldvoll

Not an UMR according to the normal definition as control flow does not depend
on uninitialized value. Repro does not show any errors with Memory Sanitizer.