Bug #80461 Accessing uninitialized memory inside strings/decimal.c ull2dec line ~1071
Submitted: 22 Feb 2016 0:16 Modified: 3 Mar 2016 23:11
Reporter: Sergey Sprogis Email Updates:
Status: Closed 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
Tags: mysqld, UMR

[22 Feb 2016 0:16] 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 ... (4 bytes) on the heap:
 ull2dec() + 0x2b4 <decimal.c : 1071>
  1071:=>  sanity(to);
 longlong2decimal() + 0x230 <decimal.c : 1108>
  1108:=>  return ull2dec(from, to);
 int2my_decimal(unsigned int, long long, char, my_decimal*)... <my_decimal.h : 438>
  438:=>   return d->check_result(mask, (unsigned_flag ?
 Item_decimal::Item_decimal(long long, bool) + 0x5ec <item.cc : 3350>
  3350:=>  int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value);
 create_schema_table(THD*, TABLE_LIST*) + 0x1658 <sql_show.cc : 7568>
  7568:=>  if (!(item= new Item_decimal((longlong) fields_info->value, false)))
 mysql_schema_table(THD*, LEX*, TABLE_LIST*) + 0x300 <sql_show.cc : 7843>
  7843:=>  if (!(table= table_list->schema_table->create_table(thd, table_list)))
 open_and_process_table(THD*, LEX*, TABLE_LIST*, ... <sql_base.cc : 4943>
  4943:=>  !check_and_update_table_version(thd, tables, tables->table->s))
 open_tables(THD*, TABLE_LIST**, unsigned int*, ... <sql_base.cc : 5663>
  5663:=>  has_prelocking_list, &ot_ctx);

Was allocated at (7968 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))))
 Query_arena::mem_calloc(unsigned long) + 0x1bc <sql_class.h : 747>
  747:=> if ((ptr=alloc_root(mem_root,size)))
 st_select_lex::add_table_to_list(THD*, Table_ident*,... <sql_parse.cc : 5819>
  5819:=>   if (!(ptr = (TABLE_LIST *) thd->mem_calloc(sizeof(TABLE_LIST))))
 make_table_list(THD*, st_select_lex*, ... <sql_show.cc : 3154>
  3154:=> if (!sel->add_table_to_list(thd,table_ident,0,0,TL_READ,MDL_SHARED_READ))
 fill_schema_table_by_open(THD*, st_mem_root*, ... <sql_show.cc : 3831>
  3831:=>   if (make_table_list(thd, lex->select_lex, db_name_lex_cstr,
 get_all_tables(THD*, TABLE_LIST*, Item*) + 0x4600 <sql_show.cc : 4640>
  4640:=> if (fill_schema_table_by_open(thd, &tmp_mem_root, FALSE,
======================================

How to repeat:
1. If you have access to Discover tool, and latest Sun Stidio 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 'low' argument located
   inside strings/decimal.c  line ~1071

  sanity(to);

   is indeed uninitialized before that point.
  
   Below is 35 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
===================================
typedef struct st_decimal_t {
 int  len;
 int *buf;
} decimal_t;
class my_decimal
 : public decimal_t
{
 int buffer[9];
 public:
  my_decimal & operator=(const my_decimal &rhs)  {
   decimal_t::operator=(rhs);
   for (unsigned i= 0; i < 9; i++) {
    buffer[i]= rhs.buffer[i];
   }
   buf= buffer;
   return *this;
  }
  void init() {
   len= 9;
   buf= buffer;
  }
  my_decimal() {
   init();
  }
};
inline void my_decimal2decimal(const my_decimal *from, my_decimal *to) {
 *to=*from;
}
int main() {
 my_decimal md1,md2, *pmd2=&md2;
 const my_decimal *pmd1=&md1;
 my_decimal2decimal(pmd1,pmd2);
 return 0;
}
===================================

Suggested fix:
Somehow initialize 'to' argument before passing its value into sanity(to);
[22 Feb 2016 12:57] Umesh Shastry
Hello Sergey,

Thank you for the report!

Thanks,
Umesh
[3 Mar 2016 23:11] Paul Dubois
Noted in 5.7.13, 5.8.0 changelogs.

The sanity() macro in strings/decimal.c produced Valgrind warnings
due to reading uninitialized buffer contents.